Convert SMC socket's getsockopt implementation to use the new getsockopt_iter callback with sockopt_t. Key changes: - Replace (char __user *optval, int __user *optlen) with sockopt_t *opt - Use opt->optlen for buffer length (input) and returned size (output) - Use copy_to_iter() instead of put_user()/copy_to_user() - Add linux/uio.h for copy_to_iter() SMC is a proxy socket: only the SOL_SMC level is handled locally, while all other levels are forwarded to the underlying CLC (TCP) socket. That socket's getsockopt() still operates on __user buffers, so the pass-through is limited to user-backed iters: optval is reconstructed from iter_out, the original optlen pointer (preserved in sockopt_t) is forwarded, and the length reported by the clcsock is mirrored back into opt->optlen so the core writes the correct value to userspace. Signed-off-by: Breno Leitao --- net/smc/af_smc.c | 41 +++++++++++++++++++++++++++++------------ net/smc/smc.h | 2 +- net/smc/smc_inet.c | 4 ++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index b5db69073e20..064d752388d2 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -3017,17 +3018,14 @@ int smc_shutdown(struct socket *sock, int how) } static int __smc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct smc_sock *smc; int val, len; smc = smc_sk(sock->sk); - if (get_user(len, optlen)) - return -EFAULT; - - len = min_t(int, len, sizeof(int)); + len = min_t(int, opt->optlen, sizeof(int)); if (len < 0) return -EINVAL; @@ -3040,9 +3038,8 @@ static int __smc_getsockopt(struct socket *sock, int level, int optname, return -EOPNOTSUPP; } - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; @@ -3168,13 +3165,26 @@ int smc_setsockopt(struct socket *sock, int level, int optname, } int smc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct smc_sock *smc; int rc; if (level == SOL_SMC) - return __smc_getsockopt(sock, level, optname, optval, optlen); + return __smc_getsockopt(sock, level, optname, opt); + + /* Other levels apply to the CLC socket, whose getsockopt() still + * operates on __user buffers. Reconstruct the userspace pointers and + * forward the call; kernel-backed callers (e.g. io_uring) are not + * supported for this pass-through. + * + * TODO: this pass-through is limited to user-backed iters because the + * underlying protocols (TCP/IP) have not been converted to + * getsockopt_iter() yet. Once they are, forward the sockopt_t directly + * and drop this restriction so all iov_iter types are supported. + */ + if (!iter_is_ubuf(&opt->iter_out) || !opt->optlen_user) + return -EOPNOTSUPP; smc = smc_sk(sock->sk); mutex_lock(&smc->clcsock_release_lock); @@ -3188,8 +3198,15 @@ int smc_getsockopt(struct socket *sock, int level, int optname, return -EOPNOTSUPP; } rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname, - optval, optlen); + opt->iter_out.ubuf, opt->optlen_user); mutex_unlock(&smc->clcsock_release_lock); + + /* The clcsock wrote the resulting length to the user optlen pointer; + * mirror it into opt->optlen so the core writes the same value back. + */ + if (get_user(opt->optlen, opt->optlen_user)) + return -EFAULT; + return rc; } @@ -3341,7 +3358,7 @@ static const struct proto_ops smc_sock_ops = { .listen = smc_listen, .shutdown = smc_shutdown, .setsockopt = smc_setsockopt, - .getsockopt = smc_getsockopt, + .getsockopt_iter = smc_getsockopt, .sendmsg = smc_sendmsg, .recvmsg = smc_recvmsg, .mmap = sock_no_mmap, diff --git a/net/smc/smc.h b/net/smc/smc.h index 52145df83f6e..e62549067b67 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -59,7 +59,7 @@ int smc_shutdown(struct socket *sock, int how); int smc_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen); int smc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen); + sockopt_t *opt); int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags); diff --git a/net/smc/smc_inet.c b/net/smc/smc_inet.c index a94084b4a498..419240fedbc3 100644 --- a/net/smc/smc_inet.c +++ b/net/smc/smc_inet.c @@ -44,7 +44,7 @@ static const struct proto_ops smc_inet_stream_ops = { .listen = smc_listen, .shutdown = smc_shutdown, .setsockopt = smc_setsockopt, - .getsockopt = smc_getsockopt, + .getsockopt_iter = smc_getsockopt, .sendmsg = smc_sendmsg, .recvmsg = smc_recvmsg, .mmap = sock_no_mmap, @@ -91,7 +91,7 @@ static const struct proto_ops smc_inet6_stream_ops = { .listen = smc_listen, .shutdown = smc_shutdown, .setsockopt = smc_setsockopt, - .getsockopt = smc_getsockopt, + .getsockopt_iter = smc_getsockopt, .sendmsg = smc_sendmsg, .recvmsg = smc_recvmsg, .mmap = sock_no_mmap, -- 2.53.0-Meta