* [PATCH net-next 0/2] net: convert UDP getsockopt to sockopt_t
@ 2026-06-12 11:45 Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 1/2] net: add sockopt_init_user() for getsockopt conversion Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t Breno Leitao
0 siblings, 2 replies; 5+ messages in thread
From: Breno Leitao @ 2026-06-12 11:45 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
The leaf proto_ops getsockopt callbacks have been moving to the new
getsockopt_iter()/sockopt_t interface.
I was trying to get SMC into getsockop and retire .getsockopt, but,
I found the best approach is to keep converting other protocols.
This series starts the same conversion one layer down, at the struct proto
getsockopt path, beginning with UDP.
Example of the current code.
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
if (level == SOL_UDP)
return udp_lib_getsockopt(sk, level, optname, optval, optlen);
return ip_getsockopt(sk, level, optname, optval, optlen);
}
We want udp_getsockopt to go to .getsockopt_iter, and there are two
approaches in this case:
1) Create a patchset that moves both of them to getsockopt_iter, which
is will be a huge change (ip_getsockopt() is used in many places)
2) Break this down, and transform from bottoms up. First
udp_lib_getsockopt() up to the point we can easily convert
others, such as ip_getsockopt().
I am taking the approach 2), so, the intermediate code will be something
like:
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
sockopt_t opt;
int err;
if (level != SOL_UDP)
return ip_getsockopt(sk, level, optname, optval, optlen);
// Convert optlen/optval in sockopt // (first patch)
err = udp_lib_getsockopt(sk, level, optname, &opt);
}
The work is bottom-up and mergeable in small steps: a protocol's inner
getsockopt helper is switched to sockopt_t behind its existing thin
__user wrapper, one patch at a time.
Once every inner helper speaks sockopt_t, a later series flips the shared
struct proto.getsockopt and inet_connection_sock_af_ops.getsockopt signatures
and drops the transitional wrappers.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (2):
net: add sockopt_init_user() for getsockopt conversion
udp: convert udp_lib_getsockopt to sockopt_t
include/linux/net.h | 23 +++++++++++++++++++++++
include/net/udp.h | 2 +-
net/ipv4/udp.c | 40 ++++++++++++++++++++++++++--------------
net/ipv6/udp.c | 17 ++++++++++++++---
4 files changed, 64 insertions(+), 18 deletions(-)
---
base-commit: c8459ee2fef502d6ef6c063751c33d9ac7943eab
change-id: 20260611-getsockopt_phase2-cd495a0115ca
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next 1/2] net: add sockopt_init_user() for getsockopt conversion
2026-06-12 11:45 [PATCH net-next 0/2] net: convert UDP getsockopt to sockopt_t Breno Leitao
@ 2026-06-12 11:45 ` Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t Breno Leitao
1 sibling, 0 replies; 5+ messages in thread
From: Breno Leitao @ 2026-06-12 11:45 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
Add a helper that initializes a user-backed sockopt_t from the
(optval, optlen) __user pair passed to a getsockopt() callback.
It is used by transitional __user getsockopt wrappers while the
proto-layer getsockopt callbacks are converted to take a sockopt_t, and
is removed once the conversion is complete.
The goal is to help to convert leafs. Example:
sock_common_getsockopt(... char __user *optval, int __user *optlen)
→ udp_getsockopt(sk, level, optname, optval__user, optlen__user)
→ udp_lib_getsockopt(sk, level, optname, &opt) /* needs a sockopt_t */
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/linux/net.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/include/linux/net.h b/include/linux/net.h
index f268f395ce473..277188a40c72e 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -47,6 +47,29 @@ typedef struct sockopt {
int optlen;
} sockopt_t;
+/*
+ * Initialize a user-backed sockopt_t from the (optval, optlen) __user pair of
+ * a getsockopt() callback. Used by transitional __user getsockopt wrappers
+ * while the proto-layer callbacks are converted to take a sockopt_t; the
+ * caller writes opt->optlen back to the user optlen after the callback.
+ */
+static inline int sockopt_init_user(sockopt_t *opt, char __user *optval,
+ int __user *optlen)
+{
+ int len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
+ iov_iter_ubuf(&opt->iter_out, ITER_DEST, optval, len);
+ iov_iter_ubuf(&opt->iter_in, ITER_SOURCE, optval, len);
+ opt->optlen = len;
+
+ return 0;
+}
+
struct poll_table_struct;
struct pipe_inode_info;
struct inode;
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t
2026-06-12 11:45 [PATCH net-next 0/2] net: convert UDP getsockopt to sockopt_t Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 1/2] net: add sockopt_init_user() for getsockopt conversion Breno Leitao
@ 2026-06-12 11:45 ` Breno Leitao
2026-06-12 14:58 ` Willem de Bruijn
1 sibling, 1 reply; 5+ messages in thread
From: Breno Leitao @ 2026-06-12 11:45 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
In preparation for converting the proto-layer getsockopt callbacks to the
sockopt_t interface, switch udp_lib_getsockopt() to take a sockopt_t.
The thin udp_getsockopt()/udpv6_getsockopt() wrappers keep their __user
signature for now: they build a user-backed sockopt_t with
sockopt_init_user(), call the helper, and write the returned length back
to optlen. The helper uses copy_to_iter() instead of copy_to_user().
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/net/udp.h | 2 +-
net/ipv4/udp.c | 40 ++++++++++++++++++++++++++--------------
net/ipv6/udp.c | 17 ++++++++++++++---
3 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/include/net/udp.h b/include/net/udp.h
index 8262e2b215b4e..1fee17274745f 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -430,7 +430,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features,
bool is_ipv6);
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen);
+ sockopt_t *opt);
int udp_lib_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsigned int optlen,
int (*push_pending_frames)(struct sock *));
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 70f6cbd4ef73b..0691f74db2c11 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -76,6 +76,7 @@
#include <linux/bpf-cgroup.h>
#include <linux/uaccess.h>
+#include <linux/uio.h>
#include <asm/ioctls.h>
#include <linux/memblock.h>
#include <linux/highmem.h>
@@ -2995,18 +2996,12 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt
}
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct udp_sock *up = udp_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
-
- if (len < 0)
- return -EINVAL;
-
- len = min_t(unsigned int, len, sizeof(int));
+ len = min_t(unsigned int, opt->optlen, sizeof(int));
switch (optname) {
case UDP_CORK:
@@ -3037,9 +3032,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- 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;
}
@@ -3047,9 +3041,27 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ip_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ /*
+ * keep the old __user pointers, until ip_getsockopt() moves
+ * to sockopt_t
+ */
+ if (level != SOL_UDP)
+ return ip_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ /* indepedendent of the err, return optlen */
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+ /* Optval was updated in copy_to_iter in udp_lib_getsockopt() */
+
+ return err;
}
/**
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194eccc..731122deed8a5 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1826,9 +1826,20 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
static int udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ipv6_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_UDP)
+ return ipv6_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+ return err;
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t
2026-06-12 11:45 ` [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t Breno Leitao
@ 2026-06-12 14:58 ` Willem de Bruijn
2026-06-12 16:28 ` Breno Leitao
0 siblings, 1 reply; 5+ messages in thread
From: Willem de Bruijn @ 2026-06-12 14:58 UTC (permalink / raw)
To: Breno Leitao, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Willem de Bruijn, Shuah Khan,
sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
Breno Leitao wrote:
> In preparation for converting the proto-layer getsockopt callbacks to the
> sockopt_t interface, switch udp_lib_getsockopt() to take a sockopt_t.
What is the benefit of this conversion?
It does add a lot more complexity and makes the code less obvious.
> The thin udp_getsockopt()/udpv6_getsockopt() wrappers keep their __user
> signature for now: they build a user-backed sockopt_t with
> sockopt_init_user(), call the helper, and write the returned length back
> to optlen. The helper uses copy_to_iter() instead of copy_to_user().
> No functional change.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
> include/net/udp.h | 2 +-
> net/ipv4/udp.c | 40 ++++++++++++++++++++++++++--------------
> net/ipv6/udp.c | 17 ++++++++++++++---
> 3 files changed, 41 insertions(+), 18 deletions(-)
>
> diff --git a/include/net/udp.h b/include/net/udp.h
> index 8262e2b215b4e..1fee17274745f 100644
> --- a/include/net/udp.h
> +++ b/include/net/udp.h
> @@ -430,7 +430,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
> netdev_features_t features,
> bool is_ipv6);
> int udp_lib_getsockopt(struct sock *sk, int level, int optname,
> - char __user *optval, int __user *optlen);
> + sockopt_t *opt);
> int udp_lib_setsockopt(struct sock *sk, int level, int optname,
> sockptr_t optval, unsigned int optlen,
> int (*push_pending_frames)(struct sock *));
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index 70f6cbd4ef73b..0691f74db2c11 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -76,6 +76,7 @@
>
> #include <linux/bpf-cgroup.h>
> #include <linux/uaccess.h>
> +#include <linux/uio.h>
> #include <asm/ioctls.h>
> #include <linux/memblock.h>
> #include <linux/highmem.h>
> @@ -2995,18 +2996,12 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt
> }
>
> int udp_lib_getsockopt(struct sock *sk, int level, int optname,
> - char __user *optval, int __user *optlen)
> + sockopt_t *opt)
> {
> struct udp_sock *up = udp_sk(sk);
> int val, len;
>
> - if (get_user(len, optlen))
> - return -EFAULT;
> -
> - if (len < 0)
> - return -EINVAL;
> -
> - len = min_t(unsigned int, len, sizeof(int));
> + len = min_t(unsigned int, opt->optlen, sizeof(int));
>
> switch (optname) {
> case UDP_CORK:
> @@ -3037,9 +3032,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
> return -ENOPROTOOPT;
> }
>
> - 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;
> }
> @@ -3047,9 +3041,27 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
> static int udp_getsockopt(struct sock *sk, int level, int optname,
> char __user *optval, int __user *optlen)
> {
> - if (level == SOL_UDP)
> - return udp_lib_getsockopt(sk, level, optname, optval, optlen);
> - return ip_getsockopt(sk, level, optname, optval, optlen);
> + sockopt_t opt;
> + int err;
> +
> + /*
> + * keep the old __user pointers, until ip_getsockopt() moves
> + * to sockopt_t
> + */
> + if (level != SOL_UDP)
> + return ip_getsockopt(sk, level, optname, optval, optlen);
> +
> + err = sockopt_init_user(&opt, optval, optlen);
> + if (err)
> + return err;
> +
> + err = udp_lib_getsockopt(sk, level, optname, &opt);
> + /* indepedendent of the err, return optlen */
typo
> + if (put_user(opt.optlen, optlen))
> + return -EFAULT;
> + /* Optval was updated in copy_to_iter in udp_lib_getsockopt() */
> +
> + return err;
> }
>
> /**
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 15e032194eccc..731122deed8a5 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -1826,9 +1826,20 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
> static int udpv6_getsockopt(struct sock *sk, int level, int optname,
> char __user *optval, int __user *optlen)
> {
> - if (level == SOL_UDP)
> - return udp_lib_getsockopt(sk, level, optname, optval, optlen);
> - return ipv6_getsockopt(sk, level, optname, optval, optlen);
> + sockopt_t opt;
> + int err;
> +
> + if (level != SOL_UDP)
> + return ipv6_getsockopt(sk, level, optname, optval, optlen);
> +
> + err = sockopt_init_user(&opt, optval, optlen);
> + if (err)
> + return err;
> +
> + err = udp_lib_getsockopt(sk, level, optname, &opt);
> + if (put_user(opt.optlen, optlen))
> + return -EFAULT;
> + return err;
> }
>
>
>
> --
> 2.53.0-Meta
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t
2026-06-12 14:58 ` Willem de Bruijn
@ 2026-06-12 16:28 ` Breno Leitao
0 siblings, 0 replies; 5+ messages in thread
From: Breno Leitao @ 2026-06-12 16:28 UTC (permalink / raw)
To: Willem de Bruijn
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan, sdf.kernel, netdev, linux-kernel,
linux-kselftest, kernel-team
Hello Willem,
On Fri, Jun 12, 2026 at 10:58:19AM -0400, Willem de Bruijn wrote:
> Breno Leitao wrote:
> > In preparation for converting the proto-layer getsockopt callbacks to the
> > sockopt_t interface, switch udp_lib_getsockopt() to take a sockopt_t.
>
> What is the benefit of this conversion?
This enables kernel callers (io_uring, bpf, etc.) to invoke getsockopt
directly. The setsockopt() conversion previously used sockptr, but
Linus objected to that approach and suggested iov_iter instead.
For full context, see:
https://lore.kernel.org/all/20260408-getsockopt-v3-0-061bb9cb355d@debian.org/
> It does add a lot more complexity and makes the code less obvious.
I agree this adds complexity. Unfortunately, I don't see a simpler way
to enable getsockopt to work with non-__user addresses.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-12 16:29 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-12 11:45 [PATCH net-next 0/2] net: convert UDP getsockopt to sockopt_t Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 1/2] net: add sockopt_init_user() for getsockopt conversion Breno Leitao
2026-06-12 11:45 ` [PATCH net-next 2/2] udp: convert udp_lib_getsockopt to sockopt_t Breno Leitao
2026-06-12 14:58 ` Willem de Bruijn
2026-06-12 16:28 ` Breno Leitao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox