From: Mr NeilBrown Date: Thu, 6 May 2021 01:27:28 +0000 (+1000) Subject: LU-14195 lnet: improve compat code for IPV6_V6ONLY sock opt X-Git-Tag: 2.14.52~63 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=6d111ff0dde182bfbdf0a7ab10df812ab43e2ddd LU-14195 lnet: improve compat code for IPV6_V6ONLY sock opt As get_fs() and set_fs() are deprecated, using them to call sock->ops->setsockopt() is not a good solution. Since linux 5.9 (v5.8-rc4-1952-ga7b75c5a8c41) it has been possible to pass a "sockptr" to ->setsockopt() which can provide a kernel address. Prior to 5.8, kernet_setsockopt() is available and should still be used. For 5.8, when neither preferred option is available, we can pass a NULL pointer which has the same effect as a pointer to zero. Fixes: 10d99554631b ("LU-13783 lnet: remove kernel_setsockopt() from lnet_sock_listen()") Signed-off-by: Mr NeilBrown Change-Id: I78c1f735a73cc9c835371c139e946144c6df5108 Reviewed-on: https://review.whamcloud.com/43559 Tested-by: jenkins Reviewed-by: James Simmons Tested-by: Maloo Reviewed-by: Arshad Hussain Reviewed-by: Chris Horn Reviewed-by: Oleg Drokin --- diff --git a/lnet/lnet/lib-socket.c b/lnet/lnet/lib-socket.c index 4a86114..d443ae4 100644 --- a/lnet/lnet/lib-socket.c +++ b/lnet/lnet/lib-socket.c @@ -343,7 +343,6 @@ struct socket * lnet_sock_listen(int local_port, int backlog, struct net *ns) { struct socket *sock; - mm_segment_t oldfs; int val = 0; int rc; @@ -360,11 +359,34 @@ lnet_sock_listen(int local_port, int backlog, struct net *ns) * This is the default, but it can be overridden so * we force it back. */ - oldfs = get_fs(); - set_fs(KERNEL_DS); - sock->ops->setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (char __user __force *) &val, sizeof(val)); - set_fs(oldfs); +#ifdef HAVE_KERNEL_SETSOCKOPT + kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &val, sizeof(val)); +#elif defined(_LINUX_SOCKPTR_H) + /* sockptr_t was introduced around v5.8-rc4-1952-ga7b75c5a8c41 + * and allows a kernel address to be passed to ->setsockopt + */ + if (ipv6_only_sock(sock->sk)) { + sockptr_t optval = KERNEL_SOCKPTR(&val); + sock->ops->setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + optval, sizeof(val)); + } +#else + /* From v5.7-rc6-2614-g5a892ff2facb when kernel_setsockopt() + * was removed until sockptr_t (above) there is no clean + * way to pass kernel address to setsockopt. We could use + * get_fs()/set_fs(), but in this particular situation there + * is an easier way. + * It depends on the fact that at least for these few kernels + * a NULL address to ipv6_setsockopt() is treated like the address + * of a zero. + */ + if (ipv6_only_sock(sock->sk) && !val) { + void *optval = NULL; + sock->ops->setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + optval, sizeof(val)); + } +#endif rc = kernel_listen(sock, backlog); if (rc == 0)