* [PATCH net-next 0/2] net/smc: convert getsockopt to getsockopt_iter
@ 2026-06-05 12:13 Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 1/2] smc: convert " Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 2/2] selftests: net: add SMC getsockopt_iter conversion test Breno Leitao
0 siblings, 2 replies; 4+ messages in thread
From: Breno Leitao @ 2026-06-05 12:13 UTC (permalink / raw)
To: D. Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
Mahanta Jambigi, Tony Lu, Wen Gu, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan
Cc: linux-rdma, linux-s390, netdev, linux-kernel, linux-kselftest,
Breno Leitao, kernel-team
SMC is the last protocol to be converted to getsockopt_iter(). As soon
as this series lands, and all the other outstanding conversion patches
land, getsockopt_iter() will be renamed back to getsockopt() and the
first part of the conversion will be complete.
This series converts SMC's getsockopt() to the new getsockopt_iter()
callback, which hands protocols a sockopt_t that abstracts the optval
buffer as an iov_iter and the option length as a plain int instead of
raw (char __user *optval, int __user *optlen) pointers. This is part of
the broader effort to move getsockopt() off __user pointers so that
kernel-backed callers (e.g. io_uring) can issue socket options.
SMC is a proxy socket: only the SOL_SMC level is handled locally, while
every other level is forwarded to the underlying CLC (TCP) socket. That
sub-socket's getsockopt() still operates on __user buffers, so the
pass-through cannot simply forward the sockopt_t. Instead it reconstructs
optval from a user-backed iter_out, forwards the preserved user optlen
pointer (kept in sockopt_t by the base patch), and mirrors the length
reported by the clcsock back into opt->optlen.
Because of that, the SOL_SMC level is fully converted and works with any
iov_iter type, but the CLC pass-through is intentionally limited to
user-backed iters (ubuf) and returns -EOPNOTSUPP otherwise. This is a
deliberate, temporary restriction: SMC will continue to operate on ubuf
only until the underlying protocols (TCP/IP) grow their own
getsockopt_iter() callbacks. Once that generic callback path is enabled,
the pass-through can forward the sockopt_t directly and drop the ubuf
restriction, supporting all iov_iter types.
The series contains:
1) smc: convert getsockopt to getsockopt_iter / sockopt_t.
2) selftests: net: I've vibe coded a kselftest exercising both the
SOL_SMC path and the CLC pass-through, including the
oversized-buffer writeback.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (2):
smc: convert to getsockopt_iter
selftests: net: add SMC getsockopt_iter conversion test
net/smc/af_smc.c | 41 +++++--
net/smc/smc.h | 2 +-
net/smc/smc_inet.c | 4 +-
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/getsockopt_smc.c | 175 +++++++++++++++++++++++++++
5 files changed, 208 insertions(+), 15 deletions(-)
---
base-commit: 0a8b288e2248cb62a62f748bc095c2136acf22b2
change-id: 20260604-getsockopt_smc-e2ad719486eb
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net-next 1/2] smc: convert to getsockopt_iter
2026-06-05 12:13 [PATCH net-next 0/2] net/smc: convert getsockopt to getsockopt_iter Breno Leitao
@ 2026-06-05 12:13 ` Breno Leitao
2026-06-08 10:48 ` Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 2/2] selftests: net: add SMC getsockopt_iter conversion test Breno Leitao
1 sibling, 1 reply; 4+ messages in thread
From: Breno Leitao @ 2026-06-05 12:13 UTC (permalink / raw)
To: D. Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
Mahanta Jambigi, Tony Lu, Wen Gu, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan
Cc: linux-rdma, linux-s390, netdev, linux-kernel, linux-kselftest,
Breno Leitao, kernel-team
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 <leitao@debian.org>
---
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 <linux/rcupdate_wait.h>
#include <linux/ctype.h>
#include <linux/splice.h>
+#include <linux/uio.h>
#include <net/sock.h>
#include <net/inet_common.h>
@@ -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
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net-next 2/2] selftests: net: add SMC getsockopt_iter conversion test
2026-06-05 12:13 [PATCH net-next 0/2] net/smc: convert getsockopt to getsockopt_iter Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 1/2] smc: convert " Breno Leitao
@ 2026-06-05 12:13 ` Breno Leitao
1 sibling, 0 replies; 4+ messages in thread
From: Breno Leitao @ 2026-06-05 12:13 UTC (permalink / raw)
To: D. Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
Mahanta Jambigi, Tony Lu, Wen Gu, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan
Cc: linux-rdma, linux-s390, netdev, linux-kernel, linux-kselftest,
Breno Leitao, kernel-team
Add a kselftest that exercises the SMC getsockopt() paths converted to
the getsockopt_iter() / sockopt_t callback:
- SOL_SMC options (SMC_LIMIT_HS), handled directly by smc_getsockopt(),
which returns the int value through copy_to_iter() and reports the
written length in opt->optlen.
- The CLC pass-through (e.g. SOL_TCP), where smc_getsockopt() forwards to
the underlying TCP socket: optval is reconstructed from iter_out, the
optlen pointer is forwarded, and the clamped length is mirrored back
through opt->optlen. The oversized-buffer case (input optlen differs
from output) specifically guards against a missing writeback sync.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/getsockopt_smc.c | 175 +++++++++++++++++++++++++++
2 files changed, 176 insertions(+)
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 5ca6c557fc3f..5b50f718dbde 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -177,6 +177,7 @@ TEST_GEN_PROGS := \
bind_wildcard \
epoll_busy_poll \
getsockopt_iter \
+ getsockopt_smc \
icmp_rfc4884 \
ipv6_fragmentation \
proc_net_pktgen \
diff --git a/tools/testing/selftests/net/getsockopt_smc.c b/tools/testing/selftests/net/getsockopt_smc.c
new file mode 100644
index 000000000000..239deefb3187
--- /dev/null
+++ b/tools/testing/selftests/net/getsockopt_smc.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Exercise the SMC getsockopt() paths that were converted to the
+ * getsockopt_iter() / sockopt_t callback.
+ *
+ * Two distinct paths are covered:
+ *
+ * - SOL_SMC options (SMC_LIMIT_HS) are handled directly by
+ * smc_getsockopt(), which returns the int value through copy_to_iter()
+ * and reports the written length in opt->optlen.
+ *
+ * - Other levels (e.g. SOL_TCP) are forwarded to the underlying CLC (TCP)
+ * socket, whose getsockopt() still operates on __user buffers. The
+ * converted smc_getsockopt() reconstructs the userspace optval from
+ * iter_out, forwards the original optlen pointer, and mirrors the length
+ * the clcsock reported back into opt->optlen so the core writes the right
+ * value to userspace.
+ *
+ * The kernel-buffer (kvec) path of the CLC pass-through returns -EOPNOTSUPP
+ * and is not reachable from a userspace getsockopt(), so it is not tested
+ * here.
+ *
+ * Author: Breno Leitao <leitao@debian.org>
+ */
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+
+#include "kselftest_harness.h"
+
+#ifndef AF_SMC
+#define AF_SMC 43
+#endif
+#ifndef SMCPROTO_SMC
+#define SMCPROTO_SMC 0
+#endif
+#ifndef SOL_SMC
+#define SOL_SMC 286
+#endif
+#ifndef SMC_LIMIT_HS
+#define SMC_LIMIT_HS 1
+#endif
+
+FIXTURE(smc) {
+ int fd;
+};
+
+FIXTURE_SETUP(smc)
+{
+ self->fd = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC);
+ if (self->fd < 0)
+ SKIP(return, "AF_SMC unavailable (errno %d) - load the smc module",
+ errno);
+}
+
+FIXTURE_TEARDOWN(smc)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+/* ---------- SOL_SMC: handled directly by smc_getsockopt() ---------- */
+
+/* SMC_LIMIT_HS is reported back as a 4-byte int via copy_to_iter(). */
+TEST_F(smc, limit_hs_default)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 0xdeadbeef;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+ EXPECT_TRUE(val == 0 || val == 1);
+}
+
+/* A value set via setsockopt() must be readable back unchanged. */
+TEST_F(smc, limit_hs_set_get)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 1;
+
+ ASSERT_EQ(0, setsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, optlen));
+
+ val = -1;
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+ EXPECT_EQ(1, val);
+}
+
+/* setsockopt() stores !!val, so a non-1 truthy value reads back as 1. */
+TEST_F(smc, limit_hs_set_get_clear)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 0;
+
+ ASSERT_EQ(0, setsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, optlen));
+
+ val = -1;
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+ EXPECT_EQ(0, val);
+}
+
+/* An oversized buffer is clamped: optlen is reported back as sizeof(int). */
+TEST_F(smc, limit_hs_oversize_clamped)
+{
+ socklen_t optlen;
+ char buf[16] = {};
+
+ optlen = sizeof(buf);
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, buf, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+}
+
+/* An unknown SOL_SMC option is rejected with -EOPNOTSUPP. */
+TEST_F(smc, bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_SMC, 0x7fff, &val, &optlen));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/* ---------- CLC pass-through: forwarded to the underlying TCP socket ------ */
+
+/* A TCP option set on the SMC socket is applied to the CLC socket and must be
+ * readable back through the pass-through, exercising optval reconstruction.
+ */
+TEST_F(smc, clc_tcp_nodelay_set_get)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 1;
+
+ ASSERT_EQ(0, setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY,
+ &val, optlen));
+
+ val = -1;
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY,
+ &val, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+ EXPECT_EQ(1, val);
+}
+
+/* With an oversized buffer the clcsock clamps the reported length to
+ * sizeof(int). That length is produced by the clcsock writing the user optlen
+ * pointer, and must be mirrored back through opt->optlen; since the input
+ * optlen (16) differs from the output (4), this fails if the writeback sync
+ * in smc_getsockopt() is missing.
+ */
+TEST_F(smc, clc_tcp_nodelay_oversize_clamped)
+{
+ socklen_t optlen;
+ char buf[16] = {};
+
+ optlen = sizeof(buf);
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY,
+ buf, &optlen));
+ EXPECT_EQ(sizeof(int), optlen);
+}
+
+/* An error from the clcsock (unknown TCP option) is propagated unchanged. */
+TEST_F(smc, clc_bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, IPPROTO_TCP, 0x7fff, &val, &optlen));
+ EXPECT_EQ(ENOPROTOOPT, errno);
+}
+
+TEST_HARNESS_MAIN
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next 1/2] smc: convert to getsockopt_iter
2026-06-05 12:13 ` [PATCH net-next 1/2] smc: convert " Breno Leitao
@ 2026-06-08 10:48 ` Breno Leitao
0 siblings, 0 replies; 4+ messages in thread
From: Breno Leitao @ 2026-06-08 10:48 UTC (permalink / raw)
To: D. Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
Mahanta Jambigi, Tony Lu, Wen Gu, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan
Cc: linux-rdma, linux-s390, netdev, linux-kernel, linux-kselftest,
kernel-team
On Fri, Jun 05, 2026 at 05:13:25AM -0700, Breno Leitao wrote:
> 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 <leitao@debian.org>
Damn, this is not building. let me send a fixed version (v2).
--
pw-bot: cr
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-08 10:49 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 12:13 [PATCH net-next 0/2] net/smc: convert getsockopt to getsockopt_iter Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 1/2] smc: convert " Breno Leitao
2026-06-08 10:48 ` Breno Leitao
2026-06-05 12:13 ` [PATCH net-next 2/2] selftests: net: add SMC getsockopt_iter conversion test Breno Leitao
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.