Netdev List
 help / color / mirror / Atom feed
* [PATCH bpf-next v3] bpf: reject short IPv4/IPv6 inputs in bpf_prog_test_run_skb
From: Sun Jian @ 2026-04-02 16:01 UTC (permalink / raw)
  To: ast, daniel, andrii, martin.lau, eddyz87, song, yonghong.song,
	john.fastabend, kpsingh, sdf, haoluo, jolsa, davem, edumazet,
	kuba, pabeni, horms, shuah
  Cc: syzbot+619b9ef527f510a57cfc, bpf, netdev, linux-kernel,
	linux-kselftest, Sun Jian

bpf_prog_test_run_skb() calls eth_type_trans() first and then uses
skb->protocol to initialize sk family and address fields for the test
run.

For IPv4 and IPv6 packets, it may access ip_hdr(skb) or ipv6_hdr(skb)
to initialize sk fields. Reject the input earlier if the Ethernet frame
carries IPv4/IPv6 EtherType but the L3 header is too short.

Fold the IPv4/IPv6 header length checks into the existing protocol
switch and return -EINVAL before accessing the network headers.

Also extend empty_skb selftests with ETH_HLEN-sized packets carrying
IPv4/IPv6 EtherType but no L3 header.

Reported-by: syzbot+619b9ef527f510a57cfc@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=619b9ef527f510a57cfc
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
---
Changes in v3:
- Rework the fix by moving the checks into the existing protocol switch
  in bpf_prog_test_run_skb() instead of duplicating the switch.
- Add empty_skb selftests for ETH_HLEN-sized packets with IPv4/IPv6
  EtherType but no L3 header.
- Retarget to bpf-next.

Link: https://lore.kernel.org/bpf/20260329161751.1914272-1-sun.jian.kdev@gmail.com/

 net/bpf/test_run.c                            | 20 ++++++++-----
 .../selftests/bpf/prog_tests/empty_skb.c      | 29 +++++++++++++++++++
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index 178c4738e63b..300e2bfc5a62 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -1120,19 +1120,23 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
 
 	switch (skb->protocol) {
 	case htons(ETH_P_IP):
-		sk->sk_family = AF_INET;
-		if (sizeof(struct iphdr) <= skb_headlen(skb)) {
-			sk->sk_rcv_saddr = ip_hdr(skb)->saddr;
-			sk->sk_daddr = ip_hdr(skb)->daddr;
+		if (skb_headlen(skb) < sizeof(struct iphdr)) {
+			ret = -EINVAL;
+			goto out;
 		}
+		sk->sk_family = AF_INET;
+		sk->sk_rcv_saddr = ip_hdr(skb)->saddr;
+		sk->sk_daddr = ip_hdr(skb)->daddr;
 		break;
 #if IS_ENABLED(CONFIG_IPV6)
 	case htons(ETH_P_IPV6):
-		sk->sk_family = AF_INET6;
-		if (sizeof(struct ipv6hdr) <= skb_headlen(skb)) {
-			sk->sk_v6_rcv_saddr = ipv6_hdr(skb)->saddr;
-			sk->sk_v6_daddr = ipv6_hdr(skb)->daddr;
+		if (skb_headlen(skb) < sizeof(struct ipv6hdr)) {
+			ret = -EINVAL;
+			goto out;
 		}
+		sk->sk_family = AF_INET6;
+		sk->sk_v6_rcv_saddr = ipv6_hdr(skb)->saddr;
+		sk->sk_v6_daddr = ipv6_hdr(skb)->daddr;
 		break;
 #endif
 	default:
diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
index 438583e1f2d1..d53567e9cd77 100644
--- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
@@ -12,6 +12,8 @@ void test_empty_skb(void)
 	struct bpf_program *prog;
 	char eth_hlen_pp[15];
 	char eth_hlen[14];
+	char ipv4_eth_hlen[14];
+	char ipv6_eth_hlen[14];
 	int veth_ifindex;
 	int ipip_ifindex;
 	int err;
@@ -46,6 +48,24 @@ void test_empty_skb(void)
 			.err = -EINVAL,
 		},
 
+		/* ETH_HLEN-sized packets with IPv4/IPv6 EtherType but
+		 * no L3 header are rejected.
+		 */
+		{
+			.msg = "veth short IPv4 ingress packet",
+			.data_in = ipv4_eth_hlen,
+			.data_size_in = sizeof(ipv4_eth_hlen),
+			.ifindex = &veth_ifindex,
+			.err = -EINVAL,
+		},
+		{
+			.msg = "veth short IPv6 ingress packet",
+			.data_in = ipv6_eth_hlen,
+			.data_size_in = sizeof(ipv6_eth_hlen),
+			.ifindex = &veth_ifindex,
+			.err = -EINVAL,
+		},
+
 		/* ETH_HLEN-sized packets:
 		 * - can not be redirected at LWT_XMIT
 		 * - can be redirected at TC to non-tunneling dest
@@ -108,6 +128,15 @@ void test_empty_skb(void)
 	SYS(out, "ip addr add 192.168.1.1/16 dev ipip0");
 	ipip_ifindex = if_nametoindex("ipip0");
 
+	memset(ipv4_eth_hlen, 0, sizeof(ipv4_eth_hlen));
+	memset(ipv6_eth_hlen, 0, sizeof(ipv6_eth_hlen));
+
+	ipv4_eth_hlen[12] = 0x08;
+	ipv4_eth_hlen[13] = 0x00;
+
+	ipv6_eth_hlen[12] = 0x86;
+	ipv6_eth_hlen[13] = 0xdd;
+
 	bpf_obj = empty_skb__open_and_load();
 	if (!ASSERT_OK_PTR(bpf_obj, "open skeleton"))
 		goto out;
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH net-next] net-timestamp: take track of the skb when wait_for_space occurs
From: Jason Xing @ 2026-04-02 16:09 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: davem, kuba, pabeni, horms, willemb, netdev, Jason Xing,
	Yushan Zhou
In-Reply-To: <CANn89i+J=OBqHFh3jWNVXm7QWhSDJ=PHtmMtzyBrTpOoNtmu5w@mail.gmail.com>

On Thu, Apr 2, 2026 at 11:40 PM Eric Dumazet <edumazet@google.com> wrote:
>
> On Thu, Apr 2, 2026 at 8:03 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> >
> > On Thu, Apr 2, 2026 at 10:24 PM Eric Dumazet <edumazet@google.com> wrote:
> > >
> > > On Thu, Apr 2, 2026 at 1:58 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> > > >
> > > > From: Jason Xing <kernelxing@tencent.com>
> > > >
> > > > Tag the skb in tcp_sendmsg_locked() when wait_for_space occurs even
> > > > though it might not carry the last byte of the sendmsg.
> > > >
> > > > If we don't do so, we might be faced with no single timestamp that
> > > > can be received by application from the error queue. The following steps
> > > > reproduce this:
> > > > 1) skb A is the current last skb before entering wait_for_space process
> > > > 2) tcp_push() pushes A without any tag
> > > > 3) A is transmitted from TCP to driver without putting any skb carring
> > > >    timestamps in the error queue, like SCHED, DRV/HARDWARE.
> > > > 4) sk_stream_wait_memory() sleeps for a while and then returns with an
> > > >    error code. Note that the socket lock is released.
> > > > 5) skb A finally gets acked and removed from the rtx queue.
> > > > 6) continue with the rest of tcp_sendmsg_locked(): it will jump to(goto)
> > > >    'do_error' label and then 'out' label.
> > > > 7) at this moment, skb A turns out to be the last one in this send
> > > >    syscall, and miss the following tcp_tx_timestamp() opportunity before
> > > >    the final tcp_push
> > > > 8) application receives no timestamps this time
> > > >
> > > > The original commit ad02c4f54782 ("tcp: provide timestamps for partial writes")
> > > > says it is best effort. Now it's time to cover the only potential point
> > > > to avoid missing record.
> > > >
> > > > The side effect is obvious that we might record more than one time for a
> > > > single send syscall since the skb that we keep track of in this scenario
> > > > might not be the last one. But tracing more than one skb is not a bad
> > > > thing since there is an emerging/promissing trend to do a detailed
> > > > packet granularity monitor.
> > >
> > > This is rather weak/lazy, especially if you do not know how long the
> > > thread is put to sleep?
> >
> > Thanks for the review!
> >
> > I actually thought about how to recognize which one should be the last
> > skb in each send syscall. But it turns out that much more complicated
> > work needs to be done which hurts the stack and common structures like
> > tcp_sock. That's why I gave up and instead chose a much simpler way to
> > minimize the impact.
> >
> > >
> > > >
> > > > Thanks to the great ID, namely, tskey, application that is responsible
> > > > for the collect/sort of timestamps leverages it to put that record in
> > > > between two consecutive send syscalls correctly.
> > > >
> > > > Signed-off-by: Yushan Zhou <katrinzhou@tencent.com>
> > > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > > ---
> > > >  net/ipv4/tcp.c | 4 +++-
> > > >  1 file changed, 3 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> > > > index 516087c622ad..2db80d75cfa4 100644
> > > > --- a/net/ipv4/tcp.c
> > > > +++ b/net/ipv4/tcp.c
> > > > @@ -1411,9 +1411,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
> > > >  wait_for_space:
> > > >                 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
> > > >                 tcp_remove_empty_skb(sk);
> > > > -               if (copied)
> > > > +               if (copied) {
> > > > +                       tcp_tx_timestamp(sk, &sockc);
> > > >                         tcp_push(sk, flags & ~MSG_MORE, mss_now,
> > > >                                  TCP_NAGLE_PUSH, size_goal);
> > > > +               }
> > > >
> > > >                 err = sk_stream_wait_memory(sk, &timeo);
> > > >                 if (err != 0)
> > >
> > > I think non blocking model should be used by modern applications,
> > > especially ones caring about timestamps.
> >
> > Please note that BPF timestamping has already been merged. It's a
> > standalone script that monitors all kinds of flows and discovers those
> > strange/unexpected behaviors. So we normally don't take a look at the
> > implementation of each application before spotting any exceptions.
> >
> > Besides, sometimes monitoring the latency of the host doesn't have
> > much to do with the mode of application. People might want to observe
> > more deeper into the underlying layer. Right now, without the patch,
> > the monitor possibly ends up missing the record of this send syscall,
> > which has been reliably reproduced by Yushan.
> >
> > >
> > > This patch has performance implications.
> > >
> > >  tcp_tx_timestamp() is quite big and was inlined because it had a single caller.
> > >
> > > After this patch, it is no longer inlined.
> > >
> > > scripts/bloat-o-meter -t vmlinux.before vmlinux.after
> > > add/remove: 2/0 grow/shrink: 0/1 up/down: 239/-192 (47)
> > > Function                                     old     new   delta
> > > tcp_tx_timestamp                               -     223    +223
> > > __pfx_tcp_tx_timestamp                         -      16     +16
> > > tcp_sendmsg_locked                          4560    4368    -192
> > > Total: Before=29652698, After=29652745, chg +0.00%
> >
> > That's right and I really appreciate your recent great effort trying
> > to mitigate the impact of function calls.
> >
> > My take is that it is a trade off. Adding one more track of the skb
> > (which adds a function call) actually doesn't hurt much especially for
> > this scenario and the last skb scenario. This path basically is not
> > that hot. There are some left things to be done to improve the
> > socket/BPF timestamping feature. And I plan to push more patches
> > (sure, very carefully) to enhance the ability of BPF timestamping to
> > observe TCP which is inevitable to add some extra functions and if
> > statements.
> >
> > I'm wondering if the above makes sense to you.
>
> If you mostly care about BPF, I would suggest:

For me, yes, I've been internally working on this stuff for a long
while and still struggling with what part is worth upstreaming. I'm
not sure how Willem thinks of the socket timestamping feature.

>
> iff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> index bd2c3c4587e133897aa057bd3b26c29df050607f..444254b8870c4406a7105cd3ce177745fb1b6c0a
> 100644
> --- a/net/ipv4/tcp.c
> +++ b/net/ipv4/tcp.c
> @@ -506,6 +506,20 @@ static void tcp_tx_timestamp(struct sock *sk,
> struct sockcm_cookie *sockc)
>                 bpf_skops_tx_timestamping(sk, skb,
> BPF_SOCK_OPS_TSTAMP_SENDMSG_CB);
>  }
>
> +static void tcp_tx_timestamp_before_wait_memory(struct sock *sk)
> +{
> +       struct sk_buff *skb;
> +
> +       if (!cgroup_bpf_enabled(CGROUP_SOCK_OPS) ||
> +           !SK_BPF_CB_FLAG_TEST(sk, SK_BPF_CB_TX_TIMESTAMPING))
> +               return;
> +
> +       skb = tcp_write_queue_tail(sk);
> +       if (skb)
> +               bpf_skops_tx_timestamping(sk, skb,
> +                                         BPF_SOCK_OPS_TSTAMP_SENDMSG_CB);
> +}
> +
>  /* @wake is one when sk_stream_write_space() calls us.
>   * This sends EPOLLOUT only if notsent_bytes is half the limit.
>   * This mimics the strategy used in sock_def_write_space().
> @@ -1403,10 +1417,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct
> msghdr *msg, size_t size)
>  wait_for_space:
>                 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
>                 tcp_remove_empty_skb(sk);
> -               if (copied)
> +               if (copied) {
> +                       tcp_tx_timestamp_before_wait_memory(sk);
>                         tcp_push(sk, flags & ~MSG_MORE, mss_now,
>                                  TCP_NAGLE_PUSH, size_goal);
> -
> +               }
>                 err = sk_stream_wait_memory(sk, &timeo);
>                 if (err != 0)
>                         goto do_error;
>

Great, thanks, Eric!!

>
> Note: You also could use a different skops to differentiate cases in
> your monitoring.
>
> $ scripts/bloat-o-meter -t vmlinux.old vmlinux.new
> add/remove: 0/0 grow/shrink: 1/0 up/down: 69/0 (69)
> Function                                     old     new   delta
> tcp_sendmsg_locked                          4848    4917     +69
> Total: Before=29651496, After=29651565, chg +0.00%

Thanks again. I will give it a try tomorrow morning :)

Thanks,
Jason

^ permalink raw reply

* Re: [PATCH net-next v2 3/3] gve: implement PTP gettimex64
From: Jordan Rhee @ 2026-04-02 16:15 UTC (permalink / raw)
  To: Naman Gulati
  Cc: Jakub Kicinski, hramamurthy, netdev, joshwash, andrew+netdev,
	davem, edumazet, pabeni, richardcochran, willemb, nktgrg, jfraker,
	ziweixiao, maolson, thostet, jefrogers, alok.a.tiwari, yyd,
	linux-kernel
In-Reply-To: <CAMP57yVS3OiFX+Qjcr9djO46OLZ=KuW2awD-7fCPZ=k2SdHNnw@mail.gmail.com>

The iowrite32be() inside gve_adminq_report_nic_ts() will prevent
reordering by the compiler because it uses volatile, and the VM exit
will prevent speculative execution by the CPU.

On Thu, Apr 2, 2026 at 8:53 AM Naman Gulati <namangulati@google.com> wrote:
>
> On Thu, Apr 2, 2026 at 8:38 AM Jordan Rhee <jordanrhee@google.com> wrote:
> >
> > We know that gve_adminq_report_nic_ts() will cause a VM exit, which
> > will act as a full barrier. Additional barriers are not strictly
> > necessary from a functional perspective. Can we rely on this fact or
> > do we need explicit barriers?
>
> I don't think that's enough. Without a barrier get_cycles can be
> speculatively executed before gve_adminq_report_nic_ts.
>
> >
> >
> > On Tue, Mar 31, 2026 at 11:05 AM Naman Gulati <namangulati@google.com> wrote:
> > >
> > > On Sun, Mar 29, 2026 at 9:31 PM Jordan Rhee <jordanrhee@google.com> wrote:
> > > >
> > > > On Sun, Mar 29, 2026 at 2:20 PM Jakub Kicinski <kuba@kernel.org> wrote:
> > > > >
> > > > > This is an AI-generated review of your patch. The human sending this
> > > > > email has considered the AI review valid, or at least plausible.
> > > > > ---
> > > > > Subject: gve: implement PTP gettimex64
> > > > >
> > > > > This commit implements the PTP gettimex64 function to enable chrony and
> > > > > phc2sys to synchronize the system clock to the NIC clock. It reads the
> > > > > NIC timestamp along with pre and post system cycle counters to minimize
> > > > > the uncertainty window.
> > > > >
> > > > > > diff --git a/drivers/net/ethernet/google/gve/gve_ptp.c b/drivers/net/ethernet/google/gve/gve_ptp.c
> > > > > > index 140b8fbce4f4..bea794541f30 100644
> > > > > > --- a/drivers/net/ethernet/google/gve/gve_ptp.c
> > > > > > +++ b/drivers/net/ethernet/google/gve/gve_ptp.c
> > > > > > @@ -10,28 +10,203 @@
> > > > >
> > > > > [ ... ]
> > > > >
> > > > > > +static int gve_adminq_read_timestamp(struct gve_priv *priv,
> > > > > > +                                  cycles_t *pre_cycles,
> > > > > > +                                  cycles_t *post_cycles)
> > > > > > +{
> > > > > > +     unsigned long delay_us = 1000;
> > > > > > +     int retry_count = 0;
> > > > > > +     int err;
> > > > > > +
> > > > > > +     lockdep_assert_held(&priv->nic_ts_read_lock);
> > > > > > +
> > > > > > +     do {
> > > > > > +             *pre_cycles = get_cycles();
> > > > > > +             err = gve_adminq_report_nic_ts(priv, priv->nic_ts_report_bus);
> > > > > > +
> > > > > > +             /* Ensure cycle counter is sampled after AdminQ cmd returns */
> > > > > > +             rmb();
> > > > > > +             *post_cycles = get_cycles();
> > > > >
> > > > > Are execution barriers needed around get_cycles() here? rmb() orders
> > > > > memory accesses, but the CPU might still speculatively execute the cycle
> > > > > counter reads out of order with respect to the AdminQ commands.
> > > > >
> > > > > Could this speculative reordering cause the strict bounds validation in
> > > > > gve_ptp_gettimex64() to fail spuriously?
> > > >
> > > > I will add mb() barriers to be totally safe.
> > >
> > > I'm not so sure that rmb() is insufficient here. In x86_64 rmb() is an
> > > lfence. lfence is used to prevent speculative execution (per the x86
> > > barrier_nospec implementation) and rdtsc_ordered, which is called by
> > > the ktime* helpers, also prefaces rdtsc with an lfence. On ARM the
> > > get_cycles implementation adds an isb instruction before reading
> > > cntvct_el0.
> > >
> > >
> > > >
> > > > > [ ... ]
> > > > >
> > > > > >  static int gve_ptp_gettimex64(struct ptp_clock_info *info,
> > > > > >                             struct timespec64 *ts,
> > > > > >                             struct ptp_system_timestamp *sts)
> > > > > >  {
> > > > > > -     return -EOPNOTSUPP;
> > > > > > +     struct gve_ptp *ptp = container_of(info, struct gve_ptp, info);
> > > > > > +     struct gve_sysclock_sample sysclock = {0};
> > > > > > +     struct gve_priv *priv = ptp->priv;
> > > > > > +     struct system_time_snapshot snap;
> > > > > > +     u64 nic_ts;
> > > > > > +     int err;
> > > > > > +
> > > > > > +     /* Take system clock snapshot before sampling cycle counters */
> > > > > > +     if (sts)
> > > > > > +             ktime_get_snapshot(&snap);
> > > > > > +
> > > > > > +     err = gve_clock_nic_ts_read(priv, &nic_ts, &sysclock);
> > > > >
> > > > > Could the system time snapshot become stale here?
> > > > >
> > > > > If priv->nic_ts_read_lock is contended, or if gve_adminq_read_timestamp()
> > > > > retries and sleeps, the snapshot might be taken significantly before the
> > > > > actual hardware read.
> > > > >
> > > > > Would it be more accurate to take the snapshot immediately before reading the
> > > > > hardware, inside the mutex?
> > > >
> > > > Ack, will fix.
> > > >
> > > > >
> > > > > > +     if (err)
> > > > > > +             return err;
> > > > > > +
> > > > > > +     if (sts) {
> > > > > > +             /* Reject samples with out of order system clock values */
> > > > > > +             if (!(sysclock.host_pre_cycles <= sysclock.nic_pre_cycles &&
> > > > > > +                   sysclock.nic_pre_cycles  <= sysclock.nic_post_cycles &&
> > > > > > +                   sysclock.nic_post_cycles <= sysclock.host_post_cycles)) {
> > > > > > +                     dev_err_ratelimited(&priv->pdev->dev,
> > > > > > +                                         "AdminQ system clock cycle counts out of order. Expecting %llu <= %llu <= %llu <= %llu\n",
> > > > > > +                                         (u64)sysclock.host_pre_cycles,
> > > > > > +                                         sysclock.nic_pre_cycles,
> > > > > > +                                         sysclock.nic_post_cycles,
> > > > > > +                                         (u64)sysclock.host_post_cycles);
> > > > > > +                     return -EBADMSG;
> > > > >
> > > > > How does this behave on architectures where get_cycles() is not implemented
> > > > > and returns 0, or with older firmware that leaves nic_pre_cycles and
> > > > > nic_post_cycles as 0?
> > > >
> > > > Firmware is not allowed to return 0. 0 should trigger the check.
> > > >
> > > > > It looks like the bounds check would unconditionally fail and return -EBADMSG.
> > > > > Since userspace tools like chrony usually expect -EOPNOTSUPP for unsupported
> > > > > extended ioctls to fall back to basic PTP ioctls, will returning -EBADMSG
> > > > > cause a hard failure for clock synchronization?
> > > >
> > > > Will fix. GVE is only supported on x86_64 and arm64, and will fail on
> > > > other architectures with -EOPNOTSUPP due to the check in
> > > > gve_cycles_to_clock_fn(). However, if an architecture returns
> > > > get_cycles() as 0, it will fail here first with -EBADMSG. I will add
> > > > an explicit check for x86 || arm64 earlier in this function.
> > > >
> > > > >
> > > > > [ ... ]
> > > > >
> > > > > > @@ -132,7 +307,7 @@ int gve_init_clock(struct gve_priv *priv)
> > > > > >               goto release_ptp;
> > > > > >       }
> > > > > >       mutex_init(&priv->nic_ts_read_lock);
> > > > > > -     err = gve_clock_nic_ts_read(priv, &nic_raw);
> > > > > > +     err = gve_clock_nic_ts_read(priv, &nic_raw, NULL);
> > > > > >       if (err) {
> > > > > >               dev_err(&priv->pdev->dev, "failed to read NIC clock %d\n", err);
> > > > > >               goto release_nic_ts_report;
> > > > >
> > > > > Is there a race condition exposing the PTP device to userspace before its
> > > > > resources are fully initialized?
> > > > >
> > > > > Looking at gve_init_clock(), gve_ptp_init() is called before allocating
> > > > > nic_ts_report and initializing nic_ts_read_lock:
> > > > >
> > > > >     err = gve_ptp_init(priv);
> > > > >     if (err)
> > > > >         return err;
> > > > >
> > > > >     priv->nic_ts_report = dma_alloc_coherent(...);
> > > > >     ...
> > > > >     mutex_init(&priv->nic_ts_read_lock);
> > > > >
> > > > > If a concurrent userspace process immediately invokes the
> > > > > PTP_SYS_OFFSET_EXTENDED ioctl after gve_ptp_init() registers the /dev/ptpX
> > > > > device, could it call gve_ptp_gettimex64() and attempt to lock the
> > > > > uninitialized mutex or dereference the NULL nic_ts_report pointer?
> > > > >
> > > > > Additionally, in the error path for gve_init_clock():
> > > > >
> > > > >     release_nic_ts_report:
> > > > >         mutex_destroy(&priv->nic_ts_read_lock);
> > > > >         dma_free_coherent(...);
> > > > >         priv->nic_ts_report = NULL;
> > > > >     release_ptp:
> > > > >         gve_ptp_release(priv);
> > > > >
> > > > > Could destroying the mutex and freeing the memory before gve_ptp_release()
> > > > > create a use-after-free window if an ioctl is currently running?
> > > >
> > > > Will be fixed in the previous patch in the series.

^ permalink raw reply

* Re: [PATCH v11 6/6] selftests: net: add TLS hardware offload test
From: Sabrina Dubroca @ 2026-04-02 16:21 UTC (permalink / raw)
  To: Rishikesh Jethwani
  Cc: netdev, saeedm, tariqt, mbloch, borisp, john.fastabend, kuba,
	davem, pabeni, edumazet, leon
In-Reply-To: <20260331163757.149343-7-rjethwani@purestorage.com>

2026-03-31, 10:37:57 -0600, Rishikesh Jethwani wrote:
> diff --git a/tools/testing/selftests/drivers/net/hw/tls_hw_offload.c b/tools/testing/selftests/drivers/net/hw/tls_hw_offload.c
> new file mode 100644
> index 000000000000..69ee834bfabd
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/net/hw/tls_hw_offload.c
[...]
> +static int do_rekey;
> +static int num_rekeys = 1;
> +static int rekeys_done;
> +static int cipher_type = 128;
> +static int tls_version = 13;

You could directly use the uapi constants (TLS_1_3_VERSION,
TLS_CIPHER_AES_GCM_128) instead of coming up with your own.

> +static int server_port = 4433;
> +static char *server_ip;
> +static int addr_family = AF_INET;
> +
> +static int send_size = 16384;
> +static int random_size_max;
> +
> +static int detect_addr_family(const char *ip)

The python code only runs IPv4 tests.
(do_client, do_server, and main also have some v6 stuff that will
never get used)


[...]
> +static void derive_key_256(struct tls12_crypto_info_aes_gcm_256 *key,
> +			   int generation)

That's almost an exact c/p of derive_key_128, except for a few s/128/256/.


[...]
> +/* Send TLS 1.3 KeyUpdate handshake message */
> +static int send_tls_key_update(int fd, int request_update)
> +{
> +	char cmsg_buf[CMSG_SPACE(sizeof(unsigned char))];
> +	unsigned char key_update_msg[5];
> +	struct msghdr msg = {0};
> +	struct cmsghdr *cmsg;
> +	struct iovec iov;
> +
> +	key_update_msg[0] = TLS_HANDSHAKE_KEY_UPDATE;
> +	key_update_msg[1] = 0;
> +	key_update_msg[2] = 0;
> +	key_update_msg[3] = 1;
> +	key_update_msg[4] = request_update ? KEY_UPDATE_REQUESTED
> +					   : KEY_UPDATE_NOT_REQUESTED;

All callers are already passing KEY_UPDATE_REQUESTED or
KEY_UPDATE_NOT_REQUESTED. But I'm not sure why you're bothering with
this, since the RX side only checks buf[0] == TLS_HANDSHAKE_KEY_UPDATE.

[...]
> +static int recv_tls_message(int fd, char *buf, size_t buflen, int *record_type)
> +{
> +	char cmsg_buf[CMSG_SPACE(sizeof(unsigned char))];
> +	struct msghdr msg = {0};
> +	struct cmsghdr *cmsg;
> +	struct iovec iov;
> +	int ret;
> +
> +	iov.iov_base = buf;
> +	iov.iov_len = buflen;
> +
> +	msg.msg_iov = &iov;
> +	msg.msg_iovlen = 1;
> +	msg.msg_control = cmsg_buf;
> +	msg.msg_controllen = sizeof(cmsg_buf);
> +
> +	ret = recvmsg(fd, &msg, 0);
> +	if (ret <= 0)
> +		return ret;
> +
> +	*record_type = TLS_RECORD_TYPE_APPLICATION_DATA;  /* default */

Is a default value actually needed? When userspace provides cmsg
space, tls_sw_recvmsg always fills it.

> +	cmsg = CMSG_FIRSTHDR(&msg);
> +	if (cmsg && cmsg->cmsg_level == SOL_TLS &&
> +	    cmsg->cmsg_type == TLS_GET_RECORD_TYPE)
> +		*record_type = *((unsigned char *)CMSG_DATA(cmsg));
> +
> +	return ret;
> +}

[...]
> +static void check_ekeyexpired(int fd)
> +{
> +	char buf[16];
> +	int ret;
> +
> +	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
> +	if (ret == -1 && errno == EKEYEXPIRED)
> +		printf("recv() returned EKEYEXPIRED as expected\n");
> +	else if (ret == -1 && errno == EAGAIN)
> +		printf("recv() returned EAGAIN (no pending data)\n");
> +	else if (ret == -1)
> +		printf("recv() returned error: %s\n", strerror(errno));

Nothing for "we expected EKEYEXPIRED but actually got data"?

And this is only for logging/debugging? It's not having on impact on
the pass/fail results.

> +}
> +
> +static int do_tls_rekey(int fd, int is_tx, int generation, int cipher)

That's almost exact c/p of setup_tls_key. Please clean up all this
duplicated code.

[...]
> +static int do_client(void)
> +{
> +	struct sockaddr_storage sa;
> +	char *buf = NULL, *echo_buf = NULL;
> +	int max_size, rekey_interval;
> +	ssize_t echo_total, echo_n;
> +	int csk = -1, ret, i, j;
> +	int test_result = 0;
> +	int current_gen = 0;
> +	int next_rekey_at;
> +	socklen_t sa_len;
> +	ssize_t n;
> +
> +	if (!server_ip) {
> +		printf("ERROR: Client requires -s <ip> option\n");
> +		return -1;
> +	}
> +
> +	max_size = random_size_max > 0 ? random_size_max : send_size;
> +	if (max_size < MIN_BUF_SIZE)
> +		max_size = MIN_BUF_SIZE;
> +	buf = malloc(max_size);
> +	echo_buf = malloc(max_size);
> +	if (!buf || !echo_buf) {
> +		printf("failed to allocate buffers\n");
> +		test_result = -1;
> +		goto out;
> +	}
> +
> +	csk = socket(addr_family, SOCK_STREAM, IPPROTO_TCP);
> +	if (csk < 0) {
> +		printf("failed to create socket: %s\n", strerror(errno));
> +		test_result = -1;
> +		goto out;
> +	}
> +
> +	memset(&sa, 0, sizeof(sa));
> +	if (addr_family == AF_INET6) {

This will never get used.

[...]

> +	if (setup_tls_key(csk, 1, 0, cipher_type) < 0 ||
> +	    setup_tls_key(csk, 0, 0, cipher_type) < 0) {

This could just use TLS_TX/TLS_RX directly instead of is_tx={0,1}.

> +		test_result = -1;
> +		goto out;
> +	}
> +
> +	if (do_rekey)
> +		printf("TLS %s setup complete. Will perform %d rekey(s).\n",
> +		       cipher_name(cipher_type), num_rekeys);
> +	else
> +		printf("TLS setup complete.\n");
> +
> +	if (random_size_max > 0)
> +		printf("Sending %d messages of random size (1..%d bytes)...\n",
> +		       TEST_ITERATIONS, random_size_max);
> +	else
> +		printf("Sending %d messages of %d bytes...\n",
> +		       TEST_ITERATIONS, send_size);
> +
> +	rekey_interval = TEST_ITERATIONS / (num_rekeys + 1);
> +	if (rekey_interval < 1)
> +		rekey_interval = 1;
> +	next_rekey_at = rekey_interval;
> +
> +	for (i = 0; i < TEST_ITERATIONS; i++) {
> +		int this_size;
> +
> +		if (random_size_max > 0)
> +			this_size = (rand() % random_size_max) + 1;
> +		else
> +			this_size = send_size;
> +
> +		for (j = 0; j < this_size; j++)
> +			buf[j] = rand() & 0xFF;
> +
> +		n = send(csk, buf, this_size, 0);
> +		if (n != this_size) {
> +			printf("FAIL: send failed: %s\n", strerror(errno));
> +			test_result = -1;
> +			break;
> +		}
> +		printf("Sent %zd bytes (iteration %d)\n", n, i + 1);
> +
> +		echo_total = 0;
> +		while (echo_total < n) {
> +			echo_n = recv(csk, echo_buf + echo_total,
> +				      n - echo_total, 0);
> +			if (echo_n < 0) {
> +				printf("FAIL: Echo recv failed: %s\n",
> +				       strerror(errno));
> +				test_result = -1;
> +				break;
> +			}
> +			if (echo_n == 0) {
> +				printf("FAIL: Connection closed during echo\n");
> +				test_result = -1;
> +				break;
> +			}
> +			echo_total += echo_n;
> +		}
> +		if (test_result != 0)
> +			break;

nit: replacing the breaks from the echo_total loop with gotos would be
more readable than this double break (but then it probably makes sense
to also replace all the "test_result = -1; break;" from the whole
TEST_ITERATIONS loop)

> +
> +		if (memcmp(buf, echo_buf, n) != 0) {
> +			printf("FAIL: Echo data mismatch!\n");
> +			test_result = -1;
> +			break;
> +		}
> +		printf("Received echo %zd bytes (ok)\n", echo_total);
> +
> +		/* Rekey at intervals: send KeyUpdate, update TX, recv KeyUpdate, update RX */
> +		if (do_rekey && rekeys_done < num_rekeys &&
> +		    (i + 1) == next_rekey_at) {
> +			current_gen++;
> +			printf("\n=== Client Rekey #%d (gen %d) ===\n",
> +			       rekeys_done + 1, current_gen);

If I'm reading this loop correctly, rekeys_done+1 is always equal to
current_gen here, since we'll either break out or also increment
rekeys_done at the end of the block.  (same in do_server)

[...]
> +static int do_server(void)
> +{
> +	struct sockaddr_storage sa;
> +	int lsk = -1, csk = -1, ret;
> +	ssize_t n, total = 0, sent;
> +	int current_gen = 0;
> +	int test_result = 0;
> +	int recv_count = 0;
> +	char *buf = NULL;
> +	int record_type;
> +	socklen_t sa_len;
> +	int max_size;
> +	int one = 1;
> +
> +	max_size = random_size_max > 0 ? random_size_max : send_size;

The server side is always reading max_size if it can. Just skip the
"random" on server? Otherwise make it receive and echo back random
amounts of bytes at each iteration.

[...]
> +	/* Main receive loop - detect KeyUpdate via MSG_PEEK + recvmsg */
> +	while (1) {
> +		n = recv(csk, buf, max_size, MSG_PEEK | MSG_DONTWAIT);
> +		if (n < 0 &&
> +		    (errno == EIO || errno == ENOMSG || errno == EAGAIN)) {

I don't think ktls ever produces ENOMSG.

> +			n = recv_tls_message(csk, buf, max_size, &record_type);
> +		} else if (n > 0) {
> +			n = recv_tls_message(csk, buf, max_size, &record_type);

Why the PEEK if you're always going to call recv_tls_message afterwards?

> +		} else if (n == 0) {
> +			printf("Connection closed by client\n");
> +			break;
> +		}
> +
> +		if (n <= 0) {
> +			if (n < 0)
> +				printf("recv failed: %s\n", strerror(errno));
> +			break;
> +		}
> +
> +		/* Handle KeyUpdate: update RX, send response, update TX */
> +		if (record_type == TLS_RECORD_TYPE_HANDSHAKE &&
> +		    n >= 1 && buf[0] == TLS_HANDSHAKE_KEY_UPDATE) {

We just excluded n <= 0, so n >= 1 is always true here.

[...]
> +		sent = send(csk, buf, n, 0);
> +		if (sent < 0) {
> +			printf("Echo send failed: %s\n", strerror(errno));
> +			break;
> +		}
> +		if (sent != n)
> +			printf("Echo partial: %zd of %zd bytes\n", sent, n);

If this happens, the client will fall out of sync with the server and
get stuck in the "echo_total < n" loop?

> +		printf("Echoed %zd bytes back to client\n", sent);
> +	}
> +
> +	printf("Connection closed. Total received: %zd bytes\n", total);
> +	if (do_rekey)
> +		printf("Rekeys completed: %d\n", rekeys_done);
> +
> +out:
> +	if (csk >= 0)
> +		close(csk);
> +	if (lsk >= 0)
> +		close(lsk);
> +	free(buf);
> +	return test_result;
> +}
> +
> +static void parse_rekey_option(const char *arg)
> +{
> +	int requested;
> +
> +	if (strncmp(arg, "--rekey=", 8) == 0) {
> +		requested = atoi(arg + 8);
> +		if (requested < 1) {
> +			printf("WARNING: Invalid rekey count, using 1\n");
> +			num_rekeys = 1;
> +		} else if (requested > MAX_REKEYS) {
> +			printf("WARNING: Rekey count %d > max %d, using %d\n",
> +			       requested, MAX_REKEYS, MAX_REKEYS);

Why not just abort the test if we get invalid input? When called
through the python wrapper we'll never get broken arguments, and if
invoked manually I think it's better to abort than do something
semi-random.

> +			num_rekeys = MAX_REKEYS;
> +		} else {
> +			num_rekeys = requested;
> +		}
> +		do_rekey = 1;
> +	} else if (strcmp(arg, "--rekey") == 0) {

The python wrapper never uses that.

> +		do_rekey = 1;
> +		num_rekeys = 1;
> +	}
> +}
> +

[...]
> +static void print_usage(const char *prog)
> +{
> +	printf("TLS Hardware Offload Two-Node Test\n\n");
> +	printf("Usage:\n");
> +	printf("  %s server [OPTIONS]\n", prog);
> +	printf("  %s client -s <ip> [OPTIONS]\n", prog);
> +	printf("\nOptions:\n");
> +	printf("  -s <ip>       Server IP to connect (client, required)\n");
> +	printf("                Supports both IPv4 and IPv6 addresses\n");
> +	printf("  -6            Use IPv6 (server only, default: IPv4)\n");
> +	printf("  -p <port>     Server port (default: 4433)\n");
> +	printf("  -b <size>     Send buffer (record) size (default: 16384)\n");

nit: not record sizes since this can go beyond the maximum record
size.


> +	printf("  -r <max>      Use random send buffer sizes (1..<max>)\n");
> +	printf("  -v <version>  TLS version: 1.2 or 1.3 (default: 1.3)\n");
> +	printf("  -c <cipher>   Cipher: 128 or 256 (default: 128)\n");
> +	printf("  --rekey[=N]   Enable rekey (default: 1, TLS 1.3 only)\n");
> +	printf("  --help        Show this help message\n");
> +	printf("\nExample (IPv4):\n");
> +	printf("  Node A: %s server\n", prog);
> +	printf("  Node B: %s client -s 192.168.20.2\n", prog);
> +	printf("\nExample (IPv6):\n");
> +	printf("  Node A: %s server -6\n", prog);
> +	printf("  Node B: %s client -s 2001:db8::1\n", prog);
> +	printf("\nRekey Example (3 rekeys, TLS 1.3 only):\n");
> +	printf("  Node A: %s server --rekey=3\n", prog);
> +	printf("  Node B: %s client -s 192.168.20.2 --rekey=3\n", prog);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int i;
> +
> +
> +	for (i = 1; i < argc; i++) {
> +		if (strcmp(argv[i], "--help") == 0 ||
> +		    strcmp(argv[i], "-h") == 0) {
> +			print_usage(argv[0]);
> +			return 0;
> +		}
> +	}
> +
> +	for (i = 1; i < argc; i++) {

I'm not a huge fan of getopt(3) but... please use it.

[...]
> +	if (tls_version == 12 && do_rekey) {
> +		printf("WARNING: TLS 1.2 does not support rekey\n");

I'd also abort the test here.



[...]
> diff --git a/tools/testing/selftests/drivers/net/hw/tls_hw_offload.py b/tools/testing/selftests/drivers/net/hw/tls_hw_offload.py
> new file mode 100755
> index 000000000000..5d14cb7d2e3c
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/net/hw/tls_hw_offload.py
> @@ -0,0 +1,281 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: GPL-2.0
> +
> +"""Test kTLS hardware offload using a C helper binary."""
> +
> +from lib.py import ksft_run, ksft_exit, ksft_pr, KsftSkipEx, ksft_true
> +from lib.py import NetDrvEpEnv
> +from lib.py import cmd, bkg, wait_port_listen, rand_port
> +import time
> +
> +
> +def check_tls_support(cfg):
> +    try:
> +        cmd("test -f /proc/net/tls_stat")
> +        cmd("test -f /proc/net/tls_stat", host=cfg.remote)
> +    except Exception as e:
> +        raise KsftSkipEx(f"kTLS not supported: {e}")
> +
> +
> +def read_tls_stats():
> +    stats = {}
> +    output = cmd("cat /proc/net/tls_stat")
> +    for line in output.stdout.strip().split('\n'):
> +        parts = line.split()
> +        if len(parts) == 2:
> +            stats[parts[0]] = int(parts[1])
> +    return stats
> +
> +
> +def verify_tls_counters(stats_before, stats_after, expected_rekeys, is_server):
> +    tx_device_diff = (stats_after.get('TlsTxDevice', 0) -
> +                      stats_before.get('TlsTxDevice', 0))

Not a big python person, but maybe use defaultdict to make this a bit
more readable?

> +    rx_device_diff = (stats_after.get('TlsRxDevice', 0) -
> +                      stats_before.get('TlsRxDevice', 0))
> +    tx_sw_diff = (stats_after.get('TlsTxSw', 0) -
> +                  stats_before.get('TlsTxSw', 0))
> +    rx_sw_diff = (stats_after.get('TlsRxSw', 0) -
> +                  stats_before.get('TlsRxSw', 0))
> +    decrypt_err_diff = (stats_after.get('TlsDecryptError', 0) -
> +                        stats_before.get('TlsDecryptError', 0))
> +
> +    used_tx_hw = tx_device_diff >= 1
> +    used_rx_hw = rx_device_diff >= 1
> +    used_tx_sw = tx_sw_diff >= 1
> +    used_rx_sw = rx_sw_diff >= 1
> +
> +    errors = 0
> +
> +    role = 'Server' if is_server else 'Client'
> +    ksft_pr(f"=== Counter Verification ({role}) ===")
> +
> +    tx_dev_before = stats_before.get('TlsTxDevice', 0)
> +    tx_dev_after = stats_after.get('TlsTxDevice', 0)
> +    ksft_pr(f"TlsTxDevice: {tx_dev_before} -> {tx_dev_after} "
> +            f"(diff: {tx_device_diff})")
> +
> +    tx_sw_before = stats_before.get('TlsTxSw', 0)
> +    tx_sw_after = stats_after.get('TlsTxSw', 0)
> +    ksft_pr(f"TlsTxSw: {tx_sw_before} -> {tx_sw_after} "
> +            f"(diff: {tx_sw_diff})")

I don't know how nice we want the selftest code to be but... this
whole function is just the same "get before/get after/diff/print"
repeated all over. Please clean this up.


[...]
> +def run_tls_test(cfg, cipher="128", tls_version="1.3", rekey=0, buffer_size=None, random_max=None):
> +    port = rand_port()
> +
> +    server_cmd = f"{cfg.bin_remote} server -p {port} -c {cipher} -v {tls_version}"
> +    if rekey > 0:
> +        server_cmd += f" --rekey={rekey}"

The rekey option has no effect on the server side.

[...]
> +def read_tls_stats_remote(cfg):
> +    stats = {}
> +    output = cmd("cat /proc/net/tls_stat", host=cfg.remote)
> +    for line in output.stdout.strip().split('\n'):
> +        parts = line.split()
> +        if len(parts) == 2:
> +            stats[parts[0]] = int(parts[1])
> +    return stats

And that's again almost exactly a c/p of read_tls_stats().


> +def test_tls_offload_basic(cfg):
> +    cfg.require_ipver("4")
> +    check_tls_support(cfg)
> +    run_tls_test(cfg, cipher="128", tls_version="1.3", rekey=0)
> +
> +
> +def test_tls_offload_aes256(cfg):
> +    cfg.require_ipver("4")
> +    check_tls_support(cfg)
> +    run_tls_test(cfg, cipher="256", tls_version="1.3", rekey=0)

Looks like you want test variants.

(tools/testing/selftests/drivers/net/README.rst "ksft_variants")


-- 
Sabrina

^ permalink raw reply

* [GIT PULL] Networking for v7.0-rc7
From: Jakub Kicinski @ 2026-04-02 16:25 UTC (permalink / raw)
  To: torvalds; +Cc: kuba, davem, netdev, linux-kernel, pabeni

Hi Linus!

The following changes since commit 453a4a5f97f0c95b7df458e6afb98d4ab057d90b:

  Merge tag 'net-7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-03-26 09:53:08 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git tags/net-7.0-rc7

for you to fetch changes up to ec7067e661193403a7a00980bda8612db5954142:

  eth: fbnic: Increase FBNIC_QUEUE_SIZE_MIN to 64 (2026-04-02 08:38:34 -0700)

----------------------------------------------------------------
With fixes from wireless, bluetooth and Netfilter included we're back
to each PR carrying 30%+ more fixes than in previous era. The good
news is that so far none of the "extra" fixes are themselves
causing real regressions. Not sure how much comfort that is.

Current release - fix to a fix:

 - netdevsim: fix build if SKB_EXTENSIONS=n

 - eth: stmmac: skip VLAN restore when VLAN hash ops are missing

Previous releases - regressions:

 - wifi: iwlwifi: mvm: don't send a 6E related command when
   not supported

Previous releases - always broken:

 - some info leak fixes

 - add missing clearing of skb->cb[] on ICMP paths from tunnels

 - ipv6: flowlabel: defer exclusive option free until RCU teardown

 - ipv6: avoid overflows in ip6_datagram_send_ctl()

 - mpls: add seqcount to protect platform_labels from OOB access

 - bridge: improve safety of parsing ND options

 - Bluetooth: fix leaks, overflows and races in hci_sync

 - netfilter: add more input validation, some to address bugs directly
   some to prevent exploits from cooking up broken configurations

 - wifi: ath: avoid poor performance due to stopping the wrong
   aggregation session

 - wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free

 - eth: fec: fix the PTP periodic output sysfs interface

 - eth: enetc: safely reinitialize TX BD ring when it has unsent frames

Signed-off-by: Jakub Kicinski <kuba@kernel.org>

----------------------------------------------------------------
Alexander Popov (1):
      wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free

Alexey Velichayshiy (1):
      wifi: iwlwifi: mvm: fix potential out-of-bounds read in iwl_mvm_nd_match_info_handler()

Buday Csaba (1):
      net: fec: fix the PTP periodic output sysfs interface

Cen Zhang (1):
      Bluetooth: SCO: fix race conditions in sco_sock_connect()

David Carlier (1):
      net: ti: icssg-prueth: fix missing data copy and wrong recycle in ZC RX dispatch

Dimitri Daskalakis (3):
      eth: fbnic: Account for page fragments when updating BDQ tail
      eth: fbnic: Fix debugfs output for BDQ's with page frags
      eth: fbnic: Increase FBNIC_QUEUE_SIZE_MIN to 64

Dipayaan Roy (1):
      net: mana: Fix RX skb truesize accounting

Emmanuel Grumbach (1):
      wifi: iwlwifi: mvm: don't send a 6E related command when not supported

Eric Dumazet (3):
      ipv6: icmp: clear skb2->cb[] in ip6_err_gen_icmpv6_unreach()
      ip6_tunnel: clear skb2->cb[] in ip4ip6_err()
      ipv6: avoid overflows in ip6_datagram_send_ctl()

Fedor Pchelkin (2):
      net: macb: fix clk handling on PCI glue driver removal
      net: macb: properly unregister fixed rate clocks

Florian Westphal (3):
      netfilter: nfnetlink_log: account for netlink header size
      netfilter: x_tables: ensure names are nul-terminated
      netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr

Guoyu Su (1):
      net: use skb_header_pointer() for TCPv4 GSO frag_off check

Hangbin Liu (1):
      ipv6: fix data race in fib6_metric_set() using cmpxchg

Jakub Kicinski (10):
      Merge tag 'wireless-2026-03-26' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless
      Merge branch 'net-enetc-safely-reinitialize-tx-bd-ring-when-it-has-unsent-frames'
      Merge branch 'fix-page-fragment-handling-when-page_size-4k'
      Merge branch 'bridge-vxlan-harden-nd-option-parsing-paths'
      Merge branch 'net-enetc-add-more-checks-to-enetc_set_rxfh'
      Merge tag 'for-net-2026-04-01' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
      Merge tag 'nf-26-04-01' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
      Merge branch 'mlx5-misc-fixes-2026-03-30'
      Merge branch 'bnxt_en-bug-fixes'
      Merge branch 'net-hsr-fixes-for-prp-duplication-and-vlan-unwind'

Jiayuan Chen (1):
      net: qrtr: replace qrtr_tx_flow radix_tree with xarray to fix memory leak

Johannes Berg (3):
      wifi: iwlwifi: mld: correctly set wifi generation data
      Merge tag 'iwlwifi-fixes-2026-03-24' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
      Merge tag 'ath-current-20260324' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath

Jonathan Rissanen (1):
      Bluetooth: hci_h4: Fix race during initialization

Keenan Dong (2):
      Bluetooth: MGMT: validate LTK enc_size on load
      Bluetooth: MGMT: validate mesh send advertising payload length

Li Xiasong (1):
      mptcp: fix soft lockup in mptcp_recvmsg()

Lorenzo Bianconi (2):
      net: airoha: Add missing cleanup bits in airoha_qdma_cleanup_rx_queue()
      net: airoha: Delay offloading until all net_devices are fully registered

Luiz Augusto von Dentz (1):
      Bluetooth: hci_sync: Fix UAF in le_read_features_complete

Luka Gejak (2):
      net: hsr: serialize seq_blocks merge across nodes
      net: hsr: fix VLAN add unwind on slave errors

Marek Behún (1):
      net: sfp: Fix Ubiquiti U-Fiber Instant SFP module on mvneta

Martin Schiller (2):
      net/x25: Fix potential double free of skb
      net/x25: Fix overflow when accumulating packets

Michael Chan (2):
      bnxt_en: Refactor some basic ring setup and adjustment logic
      bnxt_en: Don't assume XDP is never enabled in bnxt_init_dflt_ring_mode()

Michal Piekos (1):
      net: stmmac: skip VLAN restore when VLAN hash ops are missing

Oleh Konko (3):
      Bluetooth: hci_event: move wake reason storage into validated event handlers
      Bluetooth: SMP: force responder MITM requirements before building the pairing response
      Bluetooth: SMP: derive legacy responder STK authentication from MITM state

Pablo Neira Ayuso (4):
      netfilter: flowtable: strictly check for maximum number of actions
      netfilter: ctnetlink: ignore explicit helper on new expectations
      netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP
      netfilter: nf_tables: reject immediate NF_QUEUE verdict

Pagadala Yesu Anjaneyulu (1):
      wifi: iwlwifi: mld: Fix MLO scan timing

Paolo Abeni (3):
      ipv6: prevent possible UaF in addrconf_permanent_addr()
      Merge branch 'correct-bd-length-masks-and-bql-accounting-for-multi-bd-tx-packets'
      Merge branch 'net-x25-fix-overflow-and-double-free'

Pauli Virtanen (5):
      Bluetooth: hci_sync: call destroy in hci_cmd_sync_run if immediate
      Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists
      Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once fails
      Bluetooth: hci_conn: fix potential UAF in set_cig_params_sync
      Bluetooth: hci_event: fix potential UAF in hci_le_remote_conn_param_req_evt

Pavan Chebbi (1):
      bnxt_en: Restore default stat ctxs for ULP when resource is available

Pengpeng Hou (4):
      wifi: wl1251: validate packet IDs before indexing tx_frames
      net/ipv6: ioam6: prevent schema length wraparound in trace fill
      bnxt_en: set backing store type from query type
      NFC: pn533: bound the UART receive buffer

Qi Tang (2):
      netfilter: nf_conntrack_helper: pass helper to expect cleanup
      netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent

Qingfang Deng (1):
      netdevsim: fix build if SKB_EXTENSIONS=n

Reshma Immaculate Rajkumar (2):
      wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
      wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session

Sabrina Dubroca (1):
      mpls: add seqcount to protect the platform_label{,s} pair

Saeed Mahameed (2):
      net/mlx5: Avoid "No data available" when FW version queries fail
      net/mlx5: Fix switchdev mode rollback in case of failure

Shay Drory (1):
      net/mlx5: lag: Check for LAG device before creating debugfs

Srujana Challa (1):
      virtio_net: clamp rss_max_key_size to NETDEV_RSS_KEY_LEN

Stefano Garzarella (1):
      vsock: initialize child_ns_mode_locked in vsock_net_init()

Suraj Gupta (2):
      net: xilinx: axienet: Correct BD length masks to match AXIDMA IP spec
      net: xilinx: axienet: Fix BQL accounting for multi-BD TX packets

Sven Eckelmann (Plasma Cloud) (1):
      net: ethernet: mtk_ppe: avoid NULL deref when gmac0 is disabled

Thomas Bogendoerfer (1):
      tg3: Fix race for querying speed/duplex

Wei Fang (5):
      net: enetc: reset PIR and CIR if they are not equal when initializing TX ring
      net: enetc: add graceful stop to safely reinitialize the TX Ring
      net: enetc: do not access non-existent registers on pseudo MAC
      net: enetc: check whether the RSS algorithm is Toeplitz
      net: enetc: do not allow VF to configure the RSS key

Weiming Shi (1):
      rds: ib: reject FRMR registration before IB connection is established

Xiang Mei (7):
      net/sched: sch_hfsc: fix divide-by-zero in rtsc_min()
      selftests/tc-testing: add test for HFSC divide-by-zero in rtsc_min()
      net: bonding: fix use-after-free in bond_xmit_broadcast()
      bridge: mrp: reject zero test interval to avoid OOM panic
      net/sched: cls_fw: fix NULL pointer dereference on shared blocks
      net/sched: cls_flow: fix NULL pointer dereference on shared blocks
      selftests/tc-testing: add tests for cls_fw and cls_flow on shared blocks

Yang Yang (3):
      bridge: br_nd_send: linearize skb before parsing ND options
      bridge: br_nd_send: validate ND option lengths
      vxlan: validate ND option lengths in vxlan_na_create

Yasuaki Torimaru (1):
      wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation

Yifan Wu (1):
      netfilter: ipset: drop logically empty buckets in mtype_del

Yochai Eisenrich (2):
      net: ipv6: ndisc: fix ndisc_ra_useropt to initialize nduseropt_padX fields to zero to prevent an info-leak
      net: sched: cls_api: fix tc_chain_fill_node to initialize tcm_info to zero to prevent an info-leak

Yucheng Lu (1):
      net/sched: sch_netem: fix out-of-bounds access in packet corruption

Yufan Chen (1):
      net: ftgmac100: fix ring allocation unwind on open failure

Zhengchuan Liang (1):
      net: ipv6: flowlabel: defer exclusive option free until RCU teardown

hkbinbin (1):
      Bluetooth: hci_sync: fix stack buffer overflow in hci_le_big_create_sync

 drivers/bluetooth/hci_h4.c                         |   3 -
 drivers/net/bonding/bond_main.c                    |   2 +-
 drivers/net/ethernet/airoha/airoha_eth.c           |  20 ++-
 drivers/net/ethernet/airoha/airoha_eth.h           |   1 +
 drivers/net/ethernet/airoha/airoha_ppe.c           |   7 +
 drivers/net/ethernet/broadcom/bnxt/bnxt.c          |  76 +++++---
 drivers/net/ethernet/broadcom/bnxt/bnxt.h          |   1 +
 drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c  |   5 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c      |   5 +-
 drivers/net/ethernet/broadcom/tg3.c                |   2 +-
 drivers/net/ethernet/cadence/macb_pci.c            |  10 +-
 drivers/net/ethernet/faraday/ftgmac100.c           |  28 ++-
 drivers/net/ethernet/freescale/enetc/enetc.c       |  13 +-
 drivers/net/ethernet/freescale/enetc/enetc4_hw.h   |  11 ++
 drivers/net/ethernet/freescale/enetc/enetc4_pf.c   | 118 +++++++++++--
 .../net/ethernet/freescale/enetc/enetc_ethtool.c   |  10 +-
 drivers/net/ethernet/freescale/fec_ptp.c           |   3 -
 drivers/net/ethernet/mediatek/mtk_ppe_offload.c    |  21 ++-
 drivers/net/ethernet/mellanox/mlx5/core/devlink.c  |   4 +-
 .../ethernet/mellanox/mlx5/core/eswitch_offloads.c |   2 +
 drivers/net/ethernet/mellanox/mlx5/core/fw.c       |  49 ++++--
 .../net/ethernet/mellanox/mlx5/core/lag/debugfs.c  |   3 +
 .../net/ethernet/mellanox/mlx5/core/mlx5_core.h    |   4 +-
 drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c    |   2 +-
 drivers/net/ethernet/meta/fbnic/fbnic_txrx.c       |   6 +-
 drivers/net/ethernet/meta/fbnic/fbnic_txrx.h       |   2 +-
 drivers/net/ethernet/microsoft/mana/mana_en.c      |   7 +
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  14 +-
 drivers/net/ethernet/ti/icssg/icssg_common.c       |   2 +-
 drivers/net/ethernet/xilinx/xilinx_axienet.h       |   4 +-
 drivers/net/ethernet/xilinx/xilinx_axienet_main.c  |   9 +-
 drivers/net/phy/sfp.c                              |   7 +-
 drivers/net/virtio_net.c                           |  20 +--
 drivers/net/vxlan/vxlan_core.c                     |   6 +-
 drivers/net/wireless/ath/ath11k/dp_rx.c            |  15 +-
 drivers/net/wireless/ath/ath12k/dp_rx.c            |   4 +-
 .../net/wireless/intel/iwlwifi/fw/api/commands.h   |   5 +
 drivers/net/wireless/intel/iwlwifi/fw/api/scan.h   |  10 ++
 drivers/net/wireless/intel/iwlwifi/mld/iface.c     | 101 +++++++----
 drivers/net/wireless/intel/iwlwifi/mld/mac80211.c  |  19 ++
 drivers/net/wireless/intel/iwlwifi/mld/mld.c       |   1 +
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c       |   4 +-
 drivers/net/wireless/intel/iwlwifi/mld/notif.c     |   5 +
 drivers/net/wireless/intel/iwlwifi/mld/scan.c      |  30 +++-
 drivers/net/wireless/intel/iwlwifi/mld/scan.h      |   9 +-
 drivers/net/wireless/intel/iwlwifi/mvm/d3.c        |   2 +-
 drivers/net/wireless/intel/iwlwifi/mvm/fw.c        |   3 +-
 drivers/net/wireless/microchip/wilc1000/hif.c      |   2 +-
 drivers/net/wireless/ti/wl1251/tx.c                |   8 +-
 drivers/net/wireless/virtual/virt_wifi.c           |   1 -
 drivers/nfc/pn533/uart.c                           |   3 +
 include/linux/netfilter/ipset/ip_set.h             |   2 +-
 include/linux/skbuff.h                             |   1 +
 include/net/netns/mpls.h                           |   1 +
 net/bluetooth/hci_conn.c                           |   8 +-
 net/bluetooth/hci_event.c                          | 127 ++++++-------
 net/bluetooth/hci_sync.c                           |  88 ++++++---
 net/bluetooth/mgmt.c                               |  17 +-
 net/bluetooth/sco.c                                |  30 +++-
 net/bluetooth/smp.c                                |  11 +-
 net/bridge/br_arp_nd_proxy.c                       |  18 +-
 net/bridge/br_mrp_netlink.c                        |   4 +-
 net/core/dev.c                                     |  11 +-
 net/hsr/hsr_device.c                               |  32 ++--
 net/hsr/hsr_framereg.c                             |  38 +++-
 net/ipv6/addrconf.c                                |   6 +-
 net/ipv6/datagram.c                                |  10 ++
 net/ipv6/icmp.c                                    |   3 +
 net/ipv6/ioam6.c                                   |   4 +-
 net/ipv6/ip6_fib.c                                 |  14 +-
 net/ipv6/ip6_flowlabel.c                           |   5 -
 net/ipv6/ip6_tunnel.c                              |   5 +
 net/ipv6/ndisc.c                                   |   3 +
 net/mpls/af_mpls.c                                 |  29 ++-
 net/mptcp/protocol.c                               |  11 +-
 net/netfilter/ipset/ip_set_core.c                  |   4 +-
 net/netfilter/ipset/ip_set_hash_gen.h              |   2 +-
 net/netfilter/ipset/ip_set_list_set.c              |   4 +-
 net/netfilter/nf_conntrack_helper.c                |   2 +-
 net/netfilter/nf_conntrack_netlink.c               |  60 ++-----
 net/netfilter/nf_flow_table_offload.c              | 196 ++++++++++++++-------
 net/netfilter/nf_tables_api.c                      |   7 +-
 net/netfilter/nfnetlink_log.c                      |   2 +-
 net/netfilter/x_tables.c                           |  23 +++
 net/netfilter/xt_cgroup.c                          |   6 +
 net/netfilter/xt_rateest.c                         |   5 +
 net/qrtr/af_qrtr.c                                 |  31 ++--
 net/rds/ib_rdma.c                                  |   7 +-
 net/sched/cls_api.c                                |   1 +
 net/sched/cls_flow.c                               |  10 +-
 net/sched/cls_fw.c                                 |  14 +-
 net/sched/sch_hfsc.c                               |   4 +-
 net/sched/sch_netem.c                              |   5 +-
 net/vmw_vsock/af_vsock.c                           |   1 +
 net/x25/x25_in.c                                   |   9 +-
 net/x25/x25_subr.c                                 |   1 +
 .../tc-testing/tc-tests/infra/filter.json          |  44 +++++
 .../tc-testing/tc-tests/infra/qdiscs.json          |  25 +++
 98 files changed, 1147 insertions(+), 493 deletions(-)

^ permalink raw reply

* Re: [PATCH net-next 10/11] net: macb: use context swapping in .set_ringparam()
From: Théo Lebrun @ 2026-04-02 16:31 UTC (permalink / raw)
  To: Nicolai Buchwitz, Théo Lebrun
  Cc: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran,
	Russell King, Paolo Valerio, Conor Dooley, Vladimir Kondratiev,
	Gregory CLEMENT, Benoît Monin, Tawfik Bayouk,
	Thomas Petazzoni, Maxime Chevallier, netdev, linux-kernel
In-Reply-To: <90f843aa3940bdbabadddce27314c1f1@tipi-net.de>

On Thu Apr 2, 2026 at 1:29 PM CEST, Nicolai Buchwitz wrote:
> On 1.4.2026 18:39, Théo Lebrun wrote:
>> ethtool_ops.set_ringparam() is implemented using the primitive close /
>> update ring size / reopen sequence. Under memory pressure this does not
>> fly: we free our buffers at close and cannot reallocate new ones at
>> open. Also, it triggers a slow PHY reinit.
>> 
>> Instead, exploit the new context mechanism and improve our sequence to:
>>  - allocate a new context (including buffers) first
>>  - if it fails, early return without any impact to the interface
>>  - stop interface
>>  - update global state (bp, netdev, etc)
>>  - pass buffer pointers to the hardware
>>  - start interface
>>  - free old context.
>> 
>> The HW disable sequence is inspired by macb_reset_hw() but avoids
>> (1) setting NCR bit CLRSTAT and (2) clearing register PBUFRXCUT.
>> 
>> The HW re-enable sequence is inspired by macb_mac_link_up(), skipping
>> over register writes which would be redundant (because values have not
>> changed).
>> 
>> The generic context swapping parts are isolated into helper functions
>> macb_context_swap_start|end(), reusable by other operations 
>> (change_mtu,
>> set_channels, etc).
>> 
>> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
>> ---
>>  drivers/net/ethernet/cadence/macb_main.c | 89 
>> +++++++++++++++++++++++++++++---
>>  1 file changed, 82 insertions(+), 7 deletions(-)
>> 
>> diff --git a/drivers/net/ethernet/cadence/macb_main.c 
>> b/drivers/net/ethernet/cadence/macb_main.c
>> index 42b19b969f3e..543356554c11 100644
>> --- a/drivers/net/ethernet/cadence/macb_main.c
>> +++ b/drivers/net/ethernet/cadence/macb_main.c
>> @@ -2905,6 +2905,76 @@ static struct macb_context 
>> *macb_context_alloc(struct macb *bp,
>>  	return ctx;
>>  }
>> 
>> +static void macb_context_swap_start(struct macb *bp)
>> +{
>> +	struct macb_queue *queue;
>> +	unsigned int q;
>> +	u32 ctrl;
>> +
>> +	/* Disable software Tx, disable HW Tx/Rx and disable NAPI. */
>> +
>> +	netif_tx_disable(bp->netdev);
>> +
>> +	ctrl = macb_readl(bp, NCR);
>> +	macb_writel(bp, NCR, ctrl & ~(MACB_BIT(RE) | MACB_BIT(TE)));
>> +
>> +	macb_writel(bp, TSR, -1);
>> +	macb_writel(bp, RSR, -1);
>> +
>> +	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
>> +		queue_writel(queue, IDR, -1);
>> +		queue_readl(queue, ISR);
>> +		if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
>> +			queue_writel(queue, ISR, -1);
>> +	}
>> +
>> +	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
>> +		napi_disable(&queue->napi_rx);
>> +		napi_disable(&queue->napi_tx);
>> +	}
>
> tx_error_task, hresp_err_bh_work, and tx_lpi_work all dereference
> bp->ctx and could race with the pointer swap in swap_end.
> macb_close() cancels at least tx_lpi_work here. Should these be
> flushed too?

This is a large topic! While trying to find a solution as part of this
series I am noticing many race conditions. With this context series we
worsen some (by introducing bp->ctx NULL ptr dereference).

Let's start by identifying all schedule-able contexts involved:
 - #1 any request from userspace, too many callbacks to list
 - #2 NAPI softirq or kthread context, macb_{rx,tx}_poll()
 - #3 bp->hresp_err_bh_work / macb_hresp_error_task()
 - #4 bp->tx_lpi_work / macb_tx_lpi_work_fn()
 - #5 queue->tx_error_task / macb_tx_error_task()
 - #6 IRQ context, macb_interrupt()

Some race conditions:

 - #1 macb_close() doesn't cancel & wait upon #3 hresp_err_bh_work.
   They could race, especially as #3 doesn't grab bp->lock. One race
   example: #3 BP HRESP starts the interface after it has been closed
   and buffers freed. RBQP/TBQP are not reset so MACB would occur
   memory corruption on Rx and transmit memory content.

 - #1 macb_close() doesn't cancel & wait upon #5 tx_error_task. #5 does
   grab bp->lock but that doesn't make it much safer. One race example:
   same as above, restart of interface with ghost ring buffers.

 - #3 hresp_err_bh_work could collide with anything as it does no
   locking, especially #1 (xmit for example) or #2 (NAPI). It is less
   likely to collide with #6 IRQ because it starts by disabling those
   but there is a possibility of the IRQ having already triggered and
   macb_interrupt() already running in parallel of
   macb_hresp_error_task().

 - #5 queue->tx_error_task writes to Tx head/tail inside bp->lock.
   #1 macb_start_xmit() modifies those too, but inside
   queue->tx_ptr_lock. Oops. There probably are other places modifying
   head/tail or any other Tx queue value without queue->tx_ptr_lock.

 - #5 macb_tx_error_task() tries to gently disable TX but if it
    times-out then it uses the global switch (TE field in NCR
    register). That sounds racy with #2 NAPI that doesn't grab bp->lock
    and would probably break if the interface is shutdown under its
    feet.

I don't see much more. To fix all that, someone ought to exhaustively go
through all tasks (#1-6 above) & all shared data and reason one by one.
Who will be that someone? ;-) But that sounds pretty unrelated to the
series at hand, no?

I'd agree that some locking of bp->lock around the swap operation would
improve the series, and I'll add that in V2 for sure!

>
>> +}
>> +
>> +static void macb_context_swap_end(struct macb *bp,
>> +				  struct macb_context *new_ctx)
>> +{
>> +	struct macb_context *old_ctx;
>> +	struct macb_queue *queue;
>> +	unsigned int q;
>> +	u32 ctrl;
>> +
>> +	/* Swap contexts & give buffer pointers to HW. */
>> +
>> +	old_ctx = bp->ctx;
>> +	bp->ctx = new_ctx;
>> +	macb_init_buffers(bp);
>> +
>> +	/* Start NAPI, HW Tx/Rx and software Tx. */
>> +
>> +	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
>> +		napi_enable(&queue->napi_rx);
>> +		napi_enable(&queue->napi_tx);
>> +	}
>> +
>> +	if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) {
>> +		for (q = 0, queue = bp->queues; q < bp->num_queues;
>> +		     ++q, ++queue) {
>> +			queue_writel(queue, IER,
>> +				     bp->rx_intr_mask |
>> +				     MACB_TX_INT_FLAGS |
>> +				     MACB_BIT(HRESP));
>> +		}
>> +	}
>> +
>> +	ctrl = macb_readl(bp, NCR);
>> +	macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE));
>> +
>> +	netif_tx_start_all_queues(bp->netdev);
>> +
>> +	/* Free old context. */
>> +
>> +	macb_free_consistent(old_ctx);
>
> 1. kfree(old_ctx) is missing. The context struct itself leaks on
>     every swap.

Agreed.

> 2. macb_close() calls netdev_tx_reset_queue() for each queue.
>     Shouldn't the swap do the same? BQL accounting will be stale
>     after switching to a fresh context.

I explicitely left that out as I thought DQL would benefit from keeping
past context of the traffic. But indeed as we start afresh from a new
set of buffers we should reset DQL. fbnic, pointed out as an good
example by Jakub recently, does that.

>
> 3. macb_configure_dma() is not called after the swap. For
>     set_ringparam this is probably fine since rx_buffer_size
>     does not change, but this becomes a problem in patch 11.

Indeed, I had missed it took bp->ctx->rx_buffer_size as a parameter.
Will fix.

Thanks,

--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* Re: [PATCH RFC net-next v2 2/2] af_packet: Add port specific handling for HSR
From: Sebastian Andrzej Siewior @ 2026-04-02 16:32 UTC (permalink / raw)
  To: Willem de Bruijn
  Cc: netdev, (JC), Jayachandran, David S. Miller, Andrew Lunn,
	Chintan Vankar, Danish Anwar, Daolin Qiu, Eric Dumazet,
	Felix Maurer, Jakub Kicinski, Paolo Abeni, Richard Cochran,
	Simon Horman, Raghavendra, Vignesh, Bajjuri, Praneeth,
	TK, Pratheesh Gangadhar, Muralidharan, Neelima
In-Reply-To: <20260324163820.p9M2z27W@linutronix.de>

Hi Willem,

On 2026-03-24 17:38:21 [+0100], To Willem de Bruijn wrote:
> On 2026-03-19 12:27:57 [-0400], Willem de Bruijn wrote:
> > 
> > Right, so this is simple without hardware offload. Does this HW
> > offload exist, or is this aspirational. At least for infrequent PTP
> > it does not sound important.
> 
> The snippet below was for the icssg driver as-is and it works with
> updated firmware to ignore PTP packets while HW offloading is enabled.
> With the skb_ext I had, the mechanism was mostly the same but it relied
> only on the skb_ext data. Now it has to look at skb->mark.

Had you so time think about it? The proposed skb->mark solution kind of
works but feels hacky.
The version with skb_ext does touch packet's code but it is self
contained and does not slowdown the !HSR+PTP case since the compare
conditions are NOPed.
How do we move forward here?
 
Sebastian

^ permalink raw reply

* Re: [PATCH net-next 10/11] net: macb: use context swapping in .set_ringparam()
From: Théo Lebrun @ 2026-04-02 16:34 UTC (permalink / raw)
  To: Maxime Chevallier, Théo Lebrun, Nicolas Ferre,
	Claudiu Beznea, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King
  Cc: Paolo Valerio, Conor Dooley, Nicolai Buchwitz,
	Vladimir Kondratiev, Gregory CLEMENT, Benoît Monin,
	Tawfik Bayouk, Thomas Petazzoni, netdev, linux-kernel
In-Reply-To: <bc874437-1cf8-4ca6-8383-f6e3e1e888ee@bootlin.com>

On Wed Apr 1, 2026 at 10:17 PM CEST, Maxime Chevallier wrote:
> On 01/04/2026 18:39, Théo Lebrun wrote:
>> ethtool_ops.set_ringparam() is implemented using the primitive close /
>> update ring size / reopen sequence. Under memory pressure this does not
>> fly: we free our buffers at close and cannot reallocate new ones at
>> open. Also, it triggers a slow PHY reinit.
>> 
>> Instead, exploit the new context mechanism and improve our sequence to:
>>  - allocate a new context (including buffers) first
>>  - if it fails, early return without any impact to the interface
>>  - stop interface
>>  - update global state (bp, netdev, etc)
>>  - pass buffer pointers to the hardware
>>  - start interface
>>  - free old context.
>> 
>> The HW disable sequence is inspired by macb_reset_hw() but avoids
>> (1) setting NCR bit CLRSTAT and (2) clearing register PBUFRXCUT.
>> 
>> The HW re-enable sequence is inspired by macb_mac_link_up(), skipping
>> over register writes which would be redundant (because values have not
>> changed).
>> 
>> The generic context swapping parts are isolated into helper functions
>> macb_context_swap_start|end(), reusable by other operations (change_mtu,
>> set_channels, etc).
>> 
>> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
>> ---
>>  drivers/net/ethernet/cadence/macb_main.c | 89 +++++++++++++++++++++++++++++---
>>  1 file changed, 82 insertions(+), 7 deletions(-)
>> 
>> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
>> index 42b19b969f3e..543356554c11 100644
>> --- a/drivers/net/ethernet/cadence/macb_main.c
>> +++ b/drivers/net/ethernet/cadence/macb_main.c
>> @@ -2905,6 +2905,76 @@ static struct macb_context *macb_context_alloc(struct macb *bp,
>>  	return ctx;
>>  }
>>  
>> +static void macb_context_swap_start(struct macb *bp)
>> +{
>> +	struct macb_queue *queue;
>> +	unsigned int q;
>> +	u32 ctrl;
>> +
>> +	/* Disable software Tx, disable HW Tx/Rx and disable NAPI. */
>> +
>> +	netif_tx_disable(bp->netdev);
>> +
>> +	ctrl = macb_readl(bp, NCR);
>> +	macb_writel(bp, NCR, ctrl & ~(MACB_BIT(RE) | MACB_BIT(TE)));
>> +
>> +	macb_writel(bp, TSR, -1);
>> +	macb_writel(bp, RSR, -1);
>> +
>> +	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
>> +		queue_writel(queue, IDR, -1);
>> +		queue_readl(queue, ISR);
>> +		if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
>> +			queue_writel(queue, ISR, -1);
>> +	}
>
> These registers appear to be protected by bp->lock, any chance that this
> may race with an interrupt in the middle of them being configured here ?

The topic is complex! I dug deep this afternoon and replied to the
neighbour thread by Nicolai. It might be of interest to you.

https://lore.kernel.org/netdev/90f843aa3940bdbabadddce27314c1f1@tipi-net.de/
(will appear as a child to this email, it hasn't been indexed yet)

Thanks Maxime,

--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply

* [PATCH net] net: airoha: Add dma_rmb() and READ_ONCE() in airoha_qdma_rx_process()
From: Lorenzo Bianconi @ 2026-04-02 16:36 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Lorenzo Bianconi
  Cc: Xuegang Lu, linux-arm-kernel, linux-mediatek, netdev

Add missing dma_rmb() in airoha_qdma_rx_process routine to make sure the
DMA read operations are completed when the NIC reports the processing on
the current descriptor is done. Moreover, add missing READ_ONCE() in
airoha_qdma_rx_process() for DMA descriptor control fields in order to
avoid any compiler reordering.

Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 95ba99b89428e4cafb91ff7813e43ffeb38e6d9b..29dea8b35f64bfdcf88bc09fd711e0d8b4f7b6fa 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -612,15 +612,17 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
 	while (done < budget) {
 		struct airoha_queue_entry *e = &q->entry[q->tail];
 		struct airoha_qdma_desc *desc = &q->desc[q->tail];
-		u32 hash, reason, msg1 = le32_to_cpu(desc->msg1);
-		struct page *page = virt_to_head_page(e->buf);
-		u32 desc_ctrl = le32_to_cpu(desc->ctrl);
+		u32 hash, reason, msg1, desc_ctrl;
 		struct airoha_gdm_port *port;
 		int data_len, len, p;
+		struct page *page;
 
+		desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
 		if (!(desc_ctrl & QDMA_DESC_DONE_MASK))
 			break;
 
+		dma_rmb();
+
 		q->tail = (q->tail + 1) % q->ndesc;
 		q->queued--;
 
@@ -637,6 +639,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
 		if (p < 0 || !eth->ports[p])
 			goto free_frag;
 
+		page = virt_to_head_page(e->buf);
 		port = eth->ports[p];
 		if (!q->skb) { /* first buffer */
 			q->skb = napi_build_skb(e->buf, q->buf_size);
@@ -670,8 +673,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
 			 * DMA descriptor. Report DSA tag to the DSA stack
 			 * via skb dst info.
 			 */
-			u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG,
-					      le32_to_cpu(desc->msg0));
+			u32 msg0 = le32_to_cpu(READ_ONCE(desc->msg0));
+			u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, msg0);
 
 			if (sptag < ARRAY_SIZE(port->dsa_meta) &&
 			    port->dsa_meta[sptag])
@@ -679,6 +682,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
 						  &port->dsa_meta[sptag]->dst);
 		}
 
+		msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
 		hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1);
 		if (hash != AIROHA_RXD4_FOE_ENTRY)
 			skb_set_hash(q->skb, jhash_1word(hash, 0),

---
base-commit: ec7067e661193403a7a00980bda8612db5954142
change-id: 20260402-airoha_qdma_rx_process-fix-reordering-722308255b65

Best regards,
-- 
Lorenzo Bianconi <lorenzo@kernel.org>


^ permalink raw reply related

* Re: [PATCH v7 0/3] PCI: AtomicOps: Fix pci_enable_atomic_ops_to_root()
From: Bjorn Helgaas @ 2026-04-02 16:38 UTC (permalink / raw)
  To: Gerd Bayer
  Cc: Bjorn Helgaas, Jay Cornwall, Felix Kuehling, Ilpo Järvinen,
	Christian Borntraeger, Niklas Schnelle, Gerald Schaefer,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Sven Schnelle,
	Leon Romanovsky, Alexander Schmidt, linux-s390, linux-pci,
	linux-kernel, netdev, linux-rdma, stable
In-Reply-To: <20260330-fix_pciatops-v7-0-f601818417e8@linux.ibm.com>

On Mon, Mar 30, 2026 at 03:09:43PM +0200, Gerd Bayer wrote:
> Hi Bjorn et al.
> 
> On s390, AtomicOp Requests are enabled on a PCI function that supports
> them, despite the helper being ignorant about the root port's capability
> to supporting their completion.
> 
> Patch 1: Do not enable AtomicOps Requests on RCiEPs
> Patch 2: Fix the logic in pci_enable_atomic_ops_to_root()
> Patch 3: Update references to PCIe spec in that function.
> 
> I did test that the issue is fixed with these patches. Also, I verified
> that on a Mellanox/Nvidia ConnectX-6 adapter plugged straight into the
> root port of a x86 system still gets AtomicOp Requests enabled.
> 
> Due to a lack of the required hardware, I did not test this with any PCIe
> switches between root port and endpoint. So test exposure in other
> environments is highly appreciated.
> 
> Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>

Thanks, applied to pci/atomics for v7.1 with minor rework of 2/3.

> ---
> Changes in v7:
> - Prepend series with a patch to explicitly exclude RCiEPs from
>   enablement of AtomicOps Requests
> - Limit the core patch 2 to enforce a full check of the entire
>   PCIe hierarchy for support of AtomicOps capabilities.
> - Rebase to v7.0-rc6
> - Link to v6: https://lore.kernel.org/r/20260325-fix_pciatops-v6-0-10bf19d76dd1@linux.ibm.com
> 
> Changes in v6:
> - Incorporate Ilpo's editorial comments.
> - Correct logic in pci_is_atomicops_capable_rp() (annotated by Sashiko)
> - Link to v5: https://lore.kernel.org/r/20260323-fix_pciatops-v5-0-fada7233aea8@linux.ibm.com
> 
> Changes in v5:
> - Introduce new pcibios_connects_to_atomicops_capable_rc() so arch's can
>   declare AtomicOps support outside of PCIe config space. Defaults to
>   "true" - except s390.
> - rebase to 7.0-rc5
> - Link to v4: https://lore.kernel.org/r/20260313-fix_pciatops-v4-0-93bc70a63935@linux.ibm.com
> 
> Changes in v4:
> - drop patch 1 - it will become the base of a new series
> - previous patch 2, now 1: reword commit message
> - add a new patch to update references to PCI spec within
>   pci_enable_atomic_ops_to_root()
> - rebase to latest master
> - Link to v3: https://lore.kernel.org/r/20260306-fix_pciatops-v3-0-99d12bcafb19@linux.ibm.com
> 
> Changes in v3:
> - rebase to 7.0-rc2
> - gentle ping
> - add netdev and rdma lists for awareness
> - Link to v2: https://lore.kernel.org/r/20251216-fix_pciatops-v2-0-d013e9b7e2ee@linux.ibm.com
> 
> Changes in v2:
> - rebase to 6.19-rc1
> - otherwise unchanged to v1
> - Link to v1: https://lore.kernel.org/r/20251110-fix_pciatops-v1-0-edc58a57b62e@linux.ibm.com
> 
> ---
> Gerd Bayer (3):
>       PCI: AtomicOps: Do not enable requests by RCiEPs
>       PCI: AtomicOps: Do not enable without support in root port
>       PCI: AtomicOps: Update references to PCIe spec
> 
>  drivers/pci/pci.c | 48 ++++++++++++++++++++++++++----------------------
>  1 file changed, 26 insertions(+), 22 deletions(-)
> ---
> base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
> change-id: 20251106-fix_pciatops-7e8608eccb03
> 
> Best regards,
> -- 
> Gerd Bayer <gbayer@linux.ibm.com>
> 

^ permalink raw reply

* [RFC PATCH 4/6] rust: net: add minimal rtnl registration and netlink tap support
From: Wenzhao Liao @ 2026-04-02 16:36 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: linux-kernel, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	aliceryhl, tmgross, dakr, andrew+netdev, davem, edumazet, kuba,
	pabeni
In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn>

Add the minimal pieces needed to register an rtnl link driver and to
attach or detach a netlink tap from a net_device.

The abstractions model kernel-owned registration lifetime and keep the
driver layer free of unsafe blocks.

Signed-off-by: Wenzhao Liao <wenzhaoliao@ruc.edu.cn>
---
 rust/kernel/net.rs             |   2 +
 rust/kernel/net/netlink_tap.rs |  89 +++++++++++++
 rust/kernel/net/rtnl.rs        | 221 +++++++++++++++++++++++++++++++++
 3 files changed, 312 insertions(+)
 create mode 100644 rust/kernel/net/netlink_tap.rs
 create mode 100644 rust/kernel/net/rtnl.rs

diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs
index a61bc76f4499..35459816c518 100644
--- a/rust/kernel/net.rs
+++ b/rust/kernel/net.rs
@@ -5,5 +5,7 @@
 #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)]
 pub mod phy;
 pub mod netdevice;
+pub mod netlink_tap;
+pub mod rtnl;
 pub mod skbuff;
 pub mod stats;
diff --git a/rust/kernel/net/netlink_tap.rs b/rust/kernel/net/netlink_tap.rs
new file mode 100644
index 000000000000..b26461937c6c
--- /dev/null
+++ b/rust/kernel/net/netlink_tap.rs
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Netlink tap lifecycle helpers.
+//!
+//! C header: [`include/linux/netlink.h`](srctree/include/linux/netlink.h)
+
+use crate::{
+    bindings,
+    error::to_result,
+    net::netdevice,
+    prelude::*,
+    types::Opaque,
+    ThisModule,
+};
+
+/// Owns a `struct netlink_tap` plus its registration state.
+#[pin_data]
+#[derive(Zeroable)]
+pub struct Tap {
+    #[pin]
+    inner: Opaque<bindings::netlink_tap>,
+    registered: bool,
+}
+
+impl Tap {
+    /// Creates an unregistered tap.
+    pub const fn new() -> Self {
+        Self {
+            inner: Opaque::zeroed(),
+            registered: false,
+        }
+    }
+
+    /// Returns whether the tap is currently registered.
+    pub fn is_registered(&self) -> bool {
+        self.registered
+    }
+
+    /// Registers the tap for the provided device.
+    pub fn add(
+        self: Pin<&mut Self>,
+        dev: &netdevice::Device,
+        module: &'static ThisModule,
+    ) -> Result {
+        // SAFETY: The caller pinned `self`, so accessing the interior through the stable address is
+        // valid for the duration of this method.
+        let this = unsafe { self.get_unchecked_mut() };
+
+        if this.registered {
+            return Err(EBUSY);
+        }
+
+        let tap = this.inner.get();
+
+        // SAFETY: `tap` points to valid storage for `struct netlink_tap`.
+        unsafe {
+            (*tap).dev = dev.as_ptr();
+            (*tap).module = module.as_ptr();
+        }
+
+        // SAFETY: `tap` points to a valid `struct netlink_tap`.
+        to_result(unsafe { bindings::netlink_add_tap(tap) })?;
+        this.registered = true;
+        Ok(())
+    }
+
+    /// Unregisters the tap if it is currently active.
+    pub fn remove(self: Pin<&mut Self>) -> Result {
+        // SAFETY: The caller pinned `self`, so accessing the interior through the stable address is
+        // valid for the duration of this method.
+        let this = unsafe { self.get_unchecked_mut() };
+
+        if !this.registered {
+            return Ok(());
+        }
+
+        let tap = this.inner.get();
+
+        // SAFETY: `self.inner` contains a valid `struct netlink_tap` previously passed to
+        // `netlink_add_tap`.
+        to_result(unsafe { bindings::netlink_remove_tap(tap) })?;
+        this.registered = false;
+
+        // SAFETY: The tap has been removed and `netlink_remove_tap` waited for in-flight users via
+        // `synchronize_net`, so restoring the zeroed unregistered state is valid.
+        unsafe { tap.write(core::mem::zeroed()) };
+        Ok(())
+    }
+}
diff --git a/rust/kernel/net/rtnl.rs b/rust/kernel/net/rtnl.rs
new file mode 100644
index 000000000000..f3bddd29874f
--- /dev/null
+++ b/rust/kernel/net/rtnl.rs
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! RTNL link-type registration support.
+//!
+//! C headers:
+//! - [`include/net/rtnetlink.h`](srctree/include/net/rtnetlink.h)
+//! - [`include/uapi/linux/if_link.h`](srctree/include/uapi/linux/if_link.h)
+
+use crate::{
+    bindings,
+    error::{from_result, to_result},
+    net::netdevice,
+    prelude::*,
+    types::Opaque,
+};
+
+use core::{
+    marker::PhantomData,
+    mem::size_of,
+};
+
+/// Typed link-level netlink attribute selector.
+#[derive(Clone, Copy)]
+pub struct LinkAttr(usize);
+
+impl LinkAttr {
+    /// Equivalent to `IFLA_ADDRESS`.
+    pub const ADDRESS: Self = Self(bindings::IFLA_ADDRESS as usize);
+
+    const fn as_index(self) -> usize {
+        self.0
+    }
+}
+
+/// Typed info-level netlink attribute selector.
+#[derive(Clone, Copy)]
+pub struct InfoAttr(usize);
+
+impl InfoAttr {
+    /// Equivalent to `IFLA_INFO_KIND`.
+    pub const KIND: Self = Self(bindings::IFLA_INFO_KIND as usize);
+
+    /// Equivalent to `IFLA_INFO_DATA`.
+    pub const DATA: Self = Self(bindings::IFLA_INFO_DATA as usize);
+
+    const fn as_index(self) -> usize {
+        self.0
+    }
+}
+
+const LINK_ATTR_TABLE_LEN: usize = bindings::__IFLA_MAX as usize;
+const INFO_ATTR_TABLE_LEN: usize = bindings::__IFLA_INFO_MAX as usize;
+
+/// Validation context for `rtnl_link_ops::validate`.
+pub struct ValidateContext<'a> {
+    link_attrs: NlAttrTable,
+    data_attrs: NlAttrTable,
+    extack: Option<&'a mut ExtAck>,
+}
+
+impl<'a> ValidateContext<'a> {
+    fn new(
+        link_attrs: NlAttrTable,
+        data_attrs: NlAttrTable,
+        extack: Option<&'a mut ExtAck>,
+    ) -> Self {
+        Self {
+            link_attrs,
+            data_attrs,
+            extack,
+        }
+    }
+
+    /// Returns whether a link-level netlink attribute is present.
+    pub fn has_link_attr(&self, attr: LinkAttr) -> bool {
+        self.link_attrs.has_attr_index(attr.as_index())
+    }
+
+    /// Returns whether an info-level netlink attribute is present.
+    pub fn has_info_attr(&self, attr: InfoAttr) -> bool {
+        self.data_attrs.has_attr_index(attr.as_index())
+    }
+
+    /// Returns the optional extack wrapper.
+    pub fn extack(&mut self) -> Option<&mut ExtAck> {
+        self.extack.as_deref_mut()
+    }
+}
+
+/// Safe view over the `struct nlattr *tb[]` / `data[]` arrays passed to validate callbacks.
+#[derive(Clone, Copy)]
+pub struct NlAttrTable {
+    raw: *mut *mut bindings::nlattr,
+    len: usize,
+}
+
+impl NlAttrTable {
+    fn new(raw: *mut *mut bindings::nlattr, len: usize) -> Self {
+        Self { raw, len }
+    }
+
+    /// Returns `true` if the attribute slot contains a non-null pointer.
+    fn has_attr_index(&self, attr: usize) -> bool {
+        if self.raw.is_null() || attr >= self.len {
+            return false;
+        }
+
+        // SAFETY: The RTNL core provides these arrays for validate callbacks. Indexing is kept in
+        // the abstraction layer so driver code does not perform raw pointer arithmetic.
+        unsafe { !(*self.raw.add(attr)).is_null() }
+    }
+
+}
+
+/// Wrapper over `struct netlink_ext_ack`.
+#[repr(transparent)]
+pub struct ExtAck(Opaque<bindings::netlink_ext_ack>);
+
+impl ExtAck {
+    /// Creates a mutable wrapper from a raw extack pointer.
+    ///
+    /// # Safety
+    ///
+    /// The pointer must be valid for the returned lifetime.
+    pub unsafe fn from_raw<'a>(ptr: *mut bindings::netlink_ext_ack) -> &'a mut Self {
+        let ptr = ptr.cast::<Self>();
+        // SAFETY: The caller guarantees validity for the returned lifetime.
+        unsafe { &mut *ptr }
+    }
+}
+
+/// A Rust RTNL link-type driver.
+pub trait Driver: netdevice::Operations {
+    /// The RTNL link kind, e.g. `"nlmon"`.
+    const KIND: &'static CStr;
+
+    /// Performs link-type setup.
+    fn setup(dev: &mut netdevice::Device);
+
+    /// Optional netlink validation.
+    fn validate(_ctx: &mut ValidateContext<'_>) -> Result {
+        Ok(())
+    }
+}
+
+/// Owns an RTNL link-type registration.
+#[pin_data(PinnedDrop)]
+pub struct Registration<T: Driver> {
+    #[pin]
+    ops: Opaque<bindings::rtnl_link_ops>,
+    _driver: PhantomData<T>,
+}
+
+// SAFETY: Shared references do not expose interior mutation beyond drop semantics.
+unsafe impl<T: Driver> Sync for Registration<T> {}
+
+// SAFETY: Registration and unregistration are handled by RTNL core code and can be performed from
+// the module init/exit path.
+unsafe impl<T: Driver> Send for Registration<T> {}
+
+impl<T: Driver> Registration<T> {
+    extern "C" fn setup_callback(dev: *mut bindings::net_device) {
+        // SAFETY: The RTNL core only calls setup with a valid `net_device`.
+        let dev = unsafe { netdevice::Device::from_raw(dev) };
+        dev.set_netdevice_ops::<T>();
+        T::setup(dev);
+    }
+
+    extern "C" fn validate_callback(
+        tb: *mut *mut bindings::nlattr,
+        data: *mut *mut bindings::nlattr,
+        extack: *mut bindings::netlink_ext_ack,
+    ) -> c_int {
+        from_result(|| {
+            let extack = if extack.is_null() {
+                None
+            } else {
+                // SAFETY: The RTNL core passes a valid extack pointer when non-null.
+                Some(unsafe { ExtAck::from_raw(extack) })
+            };
+            let mut ctx = ValidateContext::new(
+                NlAttrTable::new(tb, LINK_ATTR_TABLE_LEN),
+                NlAttrTable::new(data, INFO_ATTR_TABLE_LEN),
+                extack,
+            );
+            T::validate(&mut ctx)?;
+            Ok(0)
+        })
+    }
+
+    /// Creates a new RTNL registration object.
+    pub fn new() -> impl PinInit<Self, Error> {
+        build_assert!(!core::mem::needs_drop::<T::Private>());
+        try_pin_init!(Self {
+            ops <- Opaque::try_ffi_init(|ptr: *mut bindings::rtnl_link_ops| {
+                // SAFETY: All-zero is a valid initial state for the opaque RTNL ops structure.
+                unsafe { ptr.write(core::mem::zeroed()) };
+
+                // SAFETY: `ptr` is valid for writes for the duration of this initializer.
+                unsafe {
+                    (*ptr).kind = T::KIND.as_char_ptr();
+                    (*ptr).priv_size = size_of::<T::Private>();
+                    (*ptr).setup = Some(Self::setup_callback);
+                    (*ptr).validate = Some(Self::validate_callback);
+                }
+
+                // SAFETY: `ptr` now points to a fully initialized `rtnl_link_ops`.
+                to_result(unsafe { bindings::rtnl_link_register(ptr) })
+            }),
+            _driver: PhantomData,
+        })
+    }
+}
+
+#[pinned_drop]
+impl<T: Driver> PinnedDrop for Registration<T> {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: The existence of `self` guarantees a successful earlier registration.
+        unsafe { bindings::rtnl_link_unregister(self.ops.get()) };
+    }
+}
-- 
2.34.1


^ permalink raw reply related

* Re: [net-next v7 07/10] net: bnxt: Implement software USO
From: Joe Damato @ 2026-04-02 16:45 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
	Jakub Kicinski, Paolo Abeni, horms, linux-kernel, leon
In-Reply-To: <CANn89i+dWeUvZW-sLU5ozZM5rHgDY1mXKOXM=EjdhJanumqVsw@mail.gmail.com>

On Wed, Apr 01, 2026 at 05:35:14PM -0700, Eric Dumazet wrote:
> On Wed, Apr 1, 2026 at 4:38 PM Joe Damato <joe@dama.to> wrote:
> >

[...]

> > +       /* Zero the csum fields so tso_build_hdr will propagate zeroes into
> > +        * every segment header. HW csum offload will recompute from scratch.
> > +        */
> 
> We might need a call to skb_cow_head(skb, 0) before changing ->check
> (or anything in skb->head)
> 
> Alternative would be to perform the clears after each tso_build_hdr()
> and leave skb->head untouched.

Thanks for the careful review; I appreciate your time and energy.

I'll remove the existing clears you pointed and perform the clear after each
tso_build_hdr() as you suggested with something like:

@@ -103,6 +96,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
                unsigned int offset;
                dma_addr_t dma_addr;
                struct tx_bd *txbd;
+               struct udphdr *uh;
                void *this_hdr;
                int bd_count;
                __le32 csum;
@@ -116,6 +110,17 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,

                tso_build_hdr(skb, this_hdr, &tso, seg_payload, last);

+               /* Zero stale csum fields copied from the original skb;
+                * HW offload recomputes from scratch.
+                */
+               uh = this_hdr + skb_transport_offset(skb);
+               uh->check = 0;
+               if (!tso.ipv6) {
+                       struct iphdr *iph = this_hdr + skb_network_offset(skb);
+
+                       iph->check = 0;
+               }

^ permalink raw reply

* [PATCH net] net: hamradio: 6pack: fix uninit-value in sixpack_receive_buf
From: Mashiro Chen @ 2026-04-02 16:45 UTC (permalink / raw)
  To: ajk, netdev
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux-hams,
	linux-kernel, Mashiro Chen, syzbot+ecdb8c9878a81eb21e54

sixpack_receive_buf() does not properly skip bytes with TTY error flags.
The while loop iterates through the flags buffer but never advances the
data pointer (cp), and passes the original count including error bytes
to sixpack_decode(). This causes sixpack_decode() to process bytes that
should have been skipped due to TTY errors.

Fix this by processing bytes one at a time, advancing cp on each
iteration, and only passing non-error bytes to sixpack_decode().
This matches the pattern used by slip_receive_buf() and
mkiss_receive_buf() for the same purpose.

Reported-by: syzbot+ecdb8c9878a81eb21e54@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=ecdb8c9878a81eb21e54
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Mashiro Chen <mashiro.chen@mailbox.org>
---
 drivers/net/hamradio/6pack.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 885992951e8a6..c8b2dc5c1becc 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -391,7 +391,6 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
 				const u8 *fp, size_t count)
 {
 	struct sixpack *sp;
-	size_t count1;
 
 	if (!count)
 		return;
@@ -401,16 +400,16 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
 		return;
 
 	/* Read the characters out of the buffer */
-	count1 = count;
-	while (count) {
-		count--;
+	while (count--) {
 		if (fp && *fp++) {
 			if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
 				sp->dev->stats.rx_errors++;
+			cp++;
 			continue;
 		}
+		sixpack_decode(sp, cp, 1);
+		cp++;
 	}
-	sixpack_decode(sp, cp, count1);
 
 	tty_unthrottle(tty);
 }
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH net v4 0/2] stmmac crash/stall fixes when under memory pressure
From: Sam Edwards @ 2026-04-02 16:53 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
	Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
	Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
	Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260402080542.293e8729@kernel.org>

On Thu, Apr 2, 2026 at 8:05 AM Jakub Kicinski <kuba@kernel.org> wrote:
> I meant we need both a threshold, and a delay :(

Hi Jakub - got it: when the critical threshold is reached, allow the
NAPI instance to sleep and start a timer instead.

1) We'd either have to leave interrupts masked or let them race
against the timer. Either one is manageable, but I feel like those
interactions carry *just* enough regression risk to bump that patch to
-next.

2) Could you point out which NAPI driver best handles this situation?
I'd like to replicate its approach.

Thanks,
Sam

^ permalink raw reply

* Re: [PATCH v2 4/4] net/rds: Use special gfp_t format specifier
From: Allison Henderson @ 2026-04-02 17:16 UTC (permalink / raw)
  To: Brendan Jackman, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Stanislaw Gruszka,
	Alexander Potapenko, Marco Elver, Dmitry Vyukov, Andrew Morton,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman
  Cc: dri-devel, linux-kernel, linux-wireless, kasan-dev, linux-mm,
	netdev, linux-rdma, rds-devel
In-Reply-To: <20260326-gfp64-v2-4-d916021cecdf@google.com>

On Thu, 2026-03-26 at 12:32 +0000, Brendan Jackman wrote:
> %pGg produces nice readable output and decouples the format string from
> the size of gfp_t.
> 
> Signed-off-by: Brendan Jackman <jackmanb@google.com>
> ---
>  net/rds/tcp_recv.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c
> index 49f96ee0c40f6..ffe843ca219c7 100644
> --- a/net/rds/tcp_recv.c
> +++ b/net/rds/tcp_recv.c
> @@ -275,7 +275,7 @@ static int rds_tcp_read_sock(struct rds_conn_path *cp, gfp_t gfp)
>  	desc.count = 1; /* give more than one skb per call */
>  
>  	tcp_read_sock(sock->sk, &desc, rds_tcp_data_recv);
> -	rdsdebug("tcp_read_sock for tc %p gfp 0x%x returned %d\n", tc, gfp,
> +	rdsdebug("tcp_read_sock for tc %p gfp %pGg returned %d\n", tc, &gfp,
>  		 desc.error);
>  
>  	if (skb_queue_empty_lockless(&sock->sk->sk_receive_queue) &&
> 
This looks fine to me. Thanks Brendan!

Acked-by: Allison Henderson <achender@kernel.org>

^ permalink raw reply

* Re: [PATCH net v4 0/2] stmmac crash/stall fixes when under memory pressure
From: Russell King (Oracle) @ 2026-04-02 17:16 UTC (permalink / raw)
  To: Sam Edwards
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maxime Coquelin, Alexandre Torgue, Maxime Chevallier,
	Ovidiu Panait, Vladimir Oltean, Baruch Siach, Serge Semin,
	Giuseppe Cavallaro, netdev, linux-stm32, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20260401041929.12392-1-CFSworks@gmail.com>

On Tue, Mar 31, 2026 at 09:19:27PM -0700, Sam Edwards wrote:
> Hi netdev,
> 
> This is v4 of my series containing a pair of bugfixes for the stmmac driver's
> receive pipeline. These issues occur when stmmac_rx_refill() does not (fully)
> succeed, which happens more frequently when free memory is low.
> 
> The first patch closes Bugzilla bug #221010 [1], where stmmac_rx() can circle
> around to a still-dirty descriptor (with a NULL buffer pointer), mistake it for
> a filled descriptor (due to OWN=0), and attempt to dereference the buffer.
> 
> In testing that patch, I discovered a second issue: starvation of available RX
> buffers causes the NIC to stop sending interrupts; if the driver stops polling,
> it will wait indefinitely for an interrupt that will never come. (Note: the
> first patch makes this issue more prominent -- mostly because it lets the
> system survive long enough to exhibit it -- but doesn't *cause* it.) The second
> patch addresses that problem as well.
> 
> Both patches are minimal, appropriate for stable, and designated to `net`. My
> focus is on small, obviously-correct, easy-to-explain changes: I'll follow up
> with another patch/series (something like [2]) for `net-next` that fixes the
> ring in a more robust way.
> 
> The tx and zc paths seem to have similar low-memory bugs, to be addressed in
> separate series.

I've tested this on my Jetson Xavier platform. One of the issues I've
had is that running iperf3 results in the receive side stalling because
it runs out of descriptors. However, despite the receive ring
eventually being re-filled and the hardware appropriately prodded, it
steadfastly refuses to restart, despite the descriptors having been
updated.

What I can see is there's 40 packets in the internal FIFOs via the
PRXQ[13:0] field of the ETH_MTLRXQxDR register.

With your patches applied:

root@tegra-ubuntu:~# iperf3 -c 192.168.248.1 -R
Connecting to host 192.168.248.1, port 5201
Reverse mode, remote host 192.168.248.1 is sending
[  5] local 192.168.248.174 port 43728 connected to 192.168.248.1 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  30.3 MBytes   254 Mbits/sec
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   2.00-3.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   3.00-4.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   4.00-5.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec
...

The remote system says:

Accepted connection from 192.168.248.174, port 43720
[  5] local 192.168.248.1 port 5201 connected to 192.168.248.174 port 43728
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  31.8 MBytes   266 Mbits/sec    1   1.41 KBytes
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec    1   1.41 KBytes
[  5]   2.00-3.00   sec  0.00 Bytes  0.00 bits/sec    0   1.41 KBytes

This is a dwmac v5.0. I think the relevant registers are:

ETH_MTLQxICSR: Value at address 0x02490d2c: 0x00000002

ETH_MTLRXQxDR: Value at address 0x02490d38: 0x00280020

 PRXQ[13:0]: Number of Packets in Receive Queue = 40
 RXQSTS[1:0]: MTL Rx Queue Fill-Level status = 2 (Rx queue fill-level
 above flow-control activate threshold)
 RRXSTS[1:0]: MTL Rx Queue Read Controller State = 0 (Idle)
 RWCSTS: MTL Rx Queue Write Controller Active Status = 0

ETH_DMADS1R: Value at address 0x0249100c: 0x00006300

 RPS0[3:0]: DMA Channel Receive Process State = Running (Waiting for Rx
 packet)

ETH_DMACxRXDTPR: Value at address 0x02491128: 0xffffe7d0

 RDT[31:0]: Receive Descriptor Tail Pointer (writes should apparently
 trigger DMA to start, but doesn't seem to)

ETH_DMACxCARXDR: Value at address 0x0249114c: 0xffffe7d0

 CURRDESAPTR[31:0]: Application receive descriptor address pointer

ETH_DMACxCARXBR: Value at address 0x0249115c: 0xffb55040

 CURRBUFAPTR[31:0]: Application receive buffer address pointer

 This makes it look like it's read the descriptor at 0x7fffffe7d0.

ETH_DMAC0SR: Value at address 0x02491160: 0x00000484

 ETI (early transmit interrupt)=1 RBU (receive buffer unavailable)=1
 TBU (transmit buffer unavailable)=1

 Clearing RBU doesn't seem to help.

Descriptors:

123 [0x0000007fffffe7b0]: 0xffbba040 0x7f 0x0 0x81000000
124 [0x0000007fffffe7c0]: 0xffbb9040 0x7f 0x0 0x81000000
125 [0x0000007fffffe7d0]: 0xffb55040 0x7f 0x0 0x81000000 <---
126 [0x0000007fffffe7e0]: 0xffc26040 0x7f 0x0 0x81000000
127 [0x0000007fffffe7f0]: 0xfff95040 0x7f 0x0 0x81000000

So they're all refilled.

Any ideas?


Full register dump via devmem2 (ethtool -d doesn't dump all registers):

Value at address 0x02490000: 0x08072203
Value at address 0x02490004: 0x00200000
Value at address 0x02490008: 0x00010404
Value at address 0x0249000c: 0x00000000
Value at address 0x02490010: 0x00040008
Value at address 0x02490014: 0x20000008
Value at address 0x02490018: 0x00000001
Value at address 0x0249001c: 0x00000001
Value at address 0x02490020: 0x00000000
Value at address 0x02490024: 0x00000000
Value at address 0x02490028: 0x00000000
Value at address 0x0249002c: 0x00000000
Value at address 0x02490030: 0x00000000
Value at address 0x02490034: 0x00000000
Value at address 0x02490038: 0x00000000
Value at address 0x0249003c: 0x00000000
Value at address 0x02490040: 0x00000000
Value at address 0x02490044: 0x00000000
Value at address 0x02490048: 0x00000000
Value at address 0x0249004c: 0x00000000
Value at address 0x02490050: 0x01600000
Value at address 0x02490054: 0x00000000
Value at address 0x02490058: 0x00000000
Value at address 0x0249005c: 0x00000000
Value at address 0x02490060: 0x00120000
Value at address 0x02490064: 0x00000000
Value at address 0x02490068: 0x00000000
Value at address 0x0249006c: 0x00000000
Value at address 0x02490070: 0xffff0002
Value at address 0x02490074: 0x00000000
Value at address 0x02490078: 0x00000000
Value at address 0x0249007c: 0x00000000
Value at address 0x02490080: 0x00000000
Value at address 0x02490084: 0x00000000
Value at address 0x02490088: 0x00000000
Value at address 0x0249008c: 0x00000000
Value at address 0x02490090: 0x00000001
Value at address 0x02490094: 0x00000000
Value at address 0x02490098: 0x00000000
Value at address 0x0249009c: 0x00000000
Value at address 0x024900a0: 0x00000002
Value at address 0x024900a4: 0x00000000
Value at address 0x024900a8: 0x00000000
Value at address 0x024900ac: 0x00000000
Value at address 0x024900b0: 0x00000001
Value at address 0x024900b4: 0x00001030
Value at address 0x024900b8: 0x00000000
Value at address 0x024900bc: 0x00000000
Value at address 0x024900c0: 0x00000000
Value at address 0x024900c4: 0x00000000
Value at address 0x024900c8: 0x00000000
Value at address 0x024900cc: 0x00000000
Value at address 0x024900d0: 0x003b0200
Value at address 0x024900d4: 0x03e8001e
Value at address 0x024900d8: 0x000f4240
Value at address 0x024900dc: 0x0000007c
Value at address 0x024900e0: 0x00000000
Value at address 0x024900e4: 0x00000000
Value at address 0x024900e8: 0x00000000
Value at address 0x024900ec: 0x00000000
Value at address 0x024900f0: 0x00000000
Value at address 0x024900f4: 0x00000000
Value at address 0x024900f8: 0x000d0000
Value at address 0x024900fc: 0x00000000
Value at address 0x02490100: 0x00000000
Value at address 0x02490104: 0x00000000
Value at address 0x02490108: 0x00000000
Value at address 0x0249010c: 0x00000000
Value at address 0x02490110: 0x00001050
Value at address 0x02490114: 0x00000000
Value at address 0x02490118: 0x00000000
Value at address 0x0249011c: 0x1bfd73f7
Value at address 0x02490120: 0x421e7a49
Value at address 0x02490124: 0x100c30c3
Value at address 0x02490128: 0x00320220
Value at address 0x02490200: 0x000e010c
Value at address 0x02490204: 0x00000006
Value at address 0x02490208: 0x00000000
Value at address 0x0249020c: 0x00000000
Value at address 0x02490210: 0x00000000
Value at address 0x02490214: 0x00000000
Value at address 0x02490218: 0x00000000
Value at address 0x0249021c: 0x00000000
Value at address 0x02490220: 0x00000000
Value at address 0x02490224: 0x00000000
Value at address 0x02490228: 0x00000000
Value at address 0x0249022c: 0x00000000
Value at address 0x02490230: 0x00000000
Value at address 0x02490234: 0x00000000
Value at address 0x02490238: 0x00000102
Value at address 0x02490d00: 0x00ff000a
Value at address 0x02490d04: 0x00000000
Value at address 0x02490d08: 0x00000000
Value at address 0x02490d0c: 0x00000000
Value at address 0x02490d10: 0x00000000
Value at address 0x02490d14: 0x00000030
Value at address 0x02490d18: 0x00000000
Value at address 0x02490d1c: 0x00000000
Value at address 0x02490d20: 0x00000000
Value at address 0x02490d24: 0x00000000
Value at address 0x02490d28: 0x00000000
Value at address 0x02490d2c: 0x00000002
Value at address 0x02490d30: 0x0ff1c4e0
Value at address 0x02490d34: 0x00000000
Value at address 0x02490d38: 0x00280020
Value at address 0x02490d3c: 0x00000000
Value at address 0x02491000: 0x00000000
Value at address 0x02491004: 0x0002180e
Value at address 0x02491008: 0x00000000
Value at address 0x0249100c: 0x00006300
Value at address 0x02491010: 0x00000000
Value at address 0x02491014: 0x00000000
Value at address 0x02491018: 0x00000000
Value at address 0x0249101c: 0x00000000
Value at address 0x02491020: 0x00000000
Value at address 0x02491024: 0x00000000
Value at address 0x02491028: 0x00000000
Value at address 0x0249102c: 0x00000000
Value at address 0x02491030: 0x00000000
Value at address 0x02491034: 0x00000000
Value at address 0x02491038: 0x00000000
Value at address 0x0249103c: 0x00000000
Value at address 0x02491040: 0x00000000
Value at address 0x02491044: 0x00000000
Value at address 0x02491048: 0x00000000
Value at address 0x0249104c: 0x00000000
Value at address 0x02491050: 0x00000000
Value at address 0x02491054: 0x00000000
Value at address 0x02491100: 0x00010000
Value at address 0x02491104: 0x00101011
Value at address 0x02491108: 0x00080c01
Value at address 0x0249110c: 0x00000000
Value at address 0x02491110: 0x0000007f
Value at address 0x02491114: 0xffffc000
Value at address 0x02491118: 0x0000007f
Value at address 0x0249111c: 0xffffe000
Value at address 0x02491120: 0xffffc500
Value at address 0x02491124: 0x00000000
Value at address 0x02491128: 0xffffe7d0
Value at address 0x0249112c: 0x000001ff
Value at address 0x02491130: 0x000001ff
Value at address 0x02491134: 0x0000d041
Value at address 0x02491138: 0x000000a0
Value at address 0x0249113c: 0x000d07c0
Value at address 0x02491140: 0x00000000
Value at address 0x02491144: 0xffffc500
Value at address 0x02491148: 0x00000000
Value at address 0x0249114c: 0xffffe7d0
Value at address 0x02491150: 0x0000007f
Value at address 0x02491154: 0xffc45b02
Value at address 0x02491158: 0x0000007f
Value at address 0x0249115c: 0xffb55040
Value at address 0x02491160: 0x00000484
Value at address 0x02491164: 0x00000000
Value at address 0x02491168: 0x00000000
Value at address 0x0249116c: 0x00000000
Value at address 0x02491170: 0x00000000
Value at address 0x02491174: 0x00000000
Value at address 0x02491178: 0x00000000
Value at address 0x0249117c: 0x00000000

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* Re: [PATCH] vsock: avoid timeout for non-blocking accept() with empty backlog
From: Bobby Eshleman @ 2026-04-02 17:19 UTC (permalink / raw)
  To: Laurence Rowe; +Cc: Stefano Garzarella, virtualization, netdev
In-Reply-To: <20260402044637.73531-1-laurencerowe@gmail.com>

On Wed, Apr 01, 2026 at 09:46:37PM -0700, Laurence Rowe wrote:
> A common pattern in epoll network servers is to eagerly accept all
> pending connections from the non-blocking listening socket after
> epoll_wait indicates the socket is ready by calling accept in a loop
> until EAGAIN is returned indicating that the backlog is empty.
> 
> Scheduling a timeout for a non-blocking accept with an empty backlog
> meant AF_VSOCK sockets used by epoll network servers incurred hundreds
> of microseconds of additional latency per accept loop compared to
> AF_INET or AF_UNIX sockets.
> 
> Signed-off-by: Laurence Rowe <laurencerowe@gmail.com>
> ---
> 
> This fixes the observed issue for me:
> 
> 1. With loopback vsock on the host running Linux v6.19.10 built with
> config-6.17.0-19-generic from Ubuntu 24.04 and make olddefconfig.
> 
> 2. With Firecracker guests with current torvalds/master, v6.19.10, and
> amazonlinux/microvm-kernel-6.1.166-24.303.amzn2023 used in Firecracker
> CI and examples. (Firecracker guest vsocks are unix sockets on the host
> side so this fix works there with just a fixed guest kernel.)
> 
> I struggled to build a generic 6.1.166 kernel that worked as a
> Firecracker guest but the patch applies (conflict due to change of
> `flags` to `arg->flags` in surrounding context) so I believe it should
> work for generic v6.1.166 kernel.
> 
> Alternatively a minimal version of this fix is to just wrap the
> `schedule_timeout` in an `if (timeout != 0)` but that leaves an
> unnecessary additional `lock_sock` call.
> 
> There are ftrace's and reproduction tools at:
> https://github.com/lrowe/linux-vsock-accept-timeout-investigation
> ---
>  net/vmw_vsock/af_vsock.c | 16 +++++++---------
>  1 file changed, 7 insertions(+), 9 deletions(-)
> 
> diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
> index 2f7d94d682..483889b6d8 100644
> --- a/net/vmw_vsock/af_vsock.c
> +++ b/net/vmw_vsock/af_vsock.c
> @@ -1850,11 +1850,11 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
>  	 * created upon connection establishment.
>  	 */
>  	timeout = sock_rcvtimeo(listener, arg->flags & O_NONBLOCK);
> -	prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
>  
>  	while ((connected = vsock_dequeue_accept(listener)) == NULL &&
> -	       listener->sk_err == 0) {
> +	       listener->sk_err == 0 && timeout != 0) {
>  		release_sock(listener);
> +		prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
>  		timeout = schedule_timeout(timeout);
>  		finish_wait(sk_sleep(listener), &wait);
>  		lock_sock(listener);
> @@ -1862,17 +1862,15 @@ static int vsock_accept(struct socket *sock, struct socket *newsock,
>  		if (signal_pending(current)) {
>  			err = sock_intr_errno(timeout);
>  			goto out;
> -		} else if (timeout == 0) {
> -			err = -EAGAIN;
> -			goto out;
>  		}
> -
> -		prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
>  	}
> -	finish_wait(sk_sleep(listener), &wait);
>  
> -	if (listener->sk_err)
> +	if (listener->sk_err) {
>  		err = -listener->sk_err;
> +	} else if (timeout == 0 && connected == NULL) {
> +		err = -EAGAIN;
> +		goto out;
> +	}

I wonder if this goto can be omitted since the following 'if
(connected)' guards the connected != NULL case? I don't have a strong
opinion, just noticed it would keep if-else symmetrical.

All-in-all, LGTM.

Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>

^ permalink raw reply

* Re: [PATCH] udp_bpf: fix use-after-free in udp_bpf_recvmsg()
From: Kuniyuki Iwashima @ 2026-04-02 17:23 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: Deepanshu Kartikey, john.fastabend, jakub, davem, dsahern,
	edumazet, kuba, pabeni, horms, ast, cong.wang, netdev, bpf,
	linux-kernel, syzbot+431f9a9e3f5227fbb904
In-Reply-To: <f64c57ad-c162-480e-910a-e7a5a3460104@linux.dev>

On Wed, Apr 1, 2026 at 11:03 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
>
> On 4/2/26 1:09 PM, Deepanshu Kartikey wrote:
> > udp_bpf_recvmsg() calls sk_msg_recvmsg() without holding lock_sock(),
> > unlike tcp_bpf_recvmsg() which properly acquires lock_sock() before
> > calling __sk_msg_recvmsg(). This allows concurrent tasks to race inside
> > sk_msg_recvmsg() on the same psock ingress queue, where one task can
> > free msg_rx via kfree_sk_msg() while another task is still reading it
> > via sk_msg_elem(), causing a slab-use-after-free.
> >
> > Fix this by adding lock_sock()/release_sock() around the sk_msg_recvmsg()
> > path in udp_bpf_recvmsg(), consistent with tcp_bpf_recvmsg(). Also make
> > udp_msg_wait_data() release lock_sock() before sleeping and reacquire it
> > after waking, so it can be called with the socket lock held, consistent
> > with how tcp_msg_wait_data() uses sk_wait_event() which does the same
> > internally.
> >
> > Note: syzbot testing shows a separate pre-existing warning:
> >    sk->sk_forward_alloc
> >    WARNING: net/ipv4/af_inet.c:162 inet_sock_destruct
> > This warning triggers from the idle CPU path (pv_native_safe_halt)
> > and is unrelated to this patch. It appears to be a pre-existing
> > memory accounting issue in the UDP sockmap path that requires
> > separate investigation.
> >
> >
> > Reported-by: syzbot+431f9a9e3f5227fbb904@syzkaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=431f9a9e3f5227fbb904
> > Fixes: 1f5be6b3b063 ("udp: Implement udp_bpf_recvmsg() for sockmap")
> > Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
> > ---
> >   net/ipv4/udp_bpf.c | 5 +++++
> >   1 file changed, 5 insertions(+)
> >
> > diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c
> > index 9f33b07b1481..f924b255cee6 100644
> > --- a/net/ipv4/udp_bpf.c
> > +++ b/net/ipv4/udp_bpf.c
> > @@ -50,7 +50,9 @@ static int udp_msg_wait_data(struct sock *sk, struct sk_psock *psock,
> >       sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
> >       ret = udp_msg_has_data(sk, psock);
> >       if (!ret) {
> > +             release_sock(sk);
> >               wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
> > +             lock_sock(sk);
> >               ret = udp_msg_has_data(sk, psock);
> >       }
> >       sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
> > @@ -79,6 +81,7 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> >               goto out;
> >       }
> >
> > +     lock_sock(sk);
> >   msg_bytes_ready:
> >       copied = sk_msg_recvmsg(sk, psock, msg, len, flags);
> >       if (!copied) {
> > @@ -90,12 +93,14 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> >               if (data) {
> >                       if (psock_has_data(psock))
> >                               goto msg_bytes_ready;
> > +                     release_sock(sk);
> >                       ret = sk_udp_recvmsg(sk, msg, len, flags);
> >                       goto out;
> >               }
> >               copied = -EAGAIN;
> >       }
> >       ret = copied;
> > +     release_sock(sk);
> >   out:
> >       sk_psock_put(sk, psock);
> >       return ret;
>
> Kuniyuki  is already working on this. Please see the
> existing discussion.
>
> https://lore.kernel.org/bpf/20260221233234.3814768-4-kuniyu@google.com/

Oh I almost forgot about this.. will respin shortly. Thanks.

^ permalink raw reply

* Re: [PATCH net v4 0/2] stmmac crash/stall fixes when under memory pressure
From: Russell King (Oracle) @ 2026-04-02 17:26 UTC (permalink / raw)
  To: Sam Edwards
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maxime Coquelin, Alexandre Torgue, Maxime Chevallier,
	Ovidiu Panait, Vladimir Oltean, Baruch Siach, Serge Semin,
	Giuseppe Cavallaro, netdev, linux-stm32, linux-arm-kernel,
	linux-kernel
In-Reply-To: <ac6kfQ98Xjt3dCGj@shell.armlinux.org.uk>

On Thu, Apr 02, 2026 at 06:16:45PM +0100, Russell King (Oracle) wrote:
> On Tue, Mar 31, 2026 at 09:19:27PM -0700, Sam Edwards wrote:
> > Hi netdev,
> > 
> > This is v4 of my series containing a pair of bugfixes for the stmmac driver's
> > receive pipeline. These issues occur when stmmac_rx_refill() does not (fully)
> > succeed, which happens more frequently when free memory is low.
> > 
> > The first patch closes Bugzilla bug #221010 [1], where stmmac_rx() can circle
> > around to a still-dirty descriptor (with a NULL buffer pointer), mistake it for
> > a filled descriptor (due to OWN=0), and attempt to dereference the buffer.
> > 
> > In testing that patch, I discovered a second issue: starvation of available RX
> > buffers causes the NIC to stop sending interrupts; if the driver stops polling,
> > it will wait indefinitely for an interrupt that will never come. (Note: the
> > first patch makes this issue more prominent -- mostly because it lets the
> > system survive long enough to exhibit it -- but doesn't *cause* it.) The second
> > patch addresses that problem as well.
> > 
> > Both patches are minimal, appropriate for stable, and designated to `net`. My
> > focus is on small, obviously-correct, easy-to-explain changes: I'll follow up
> > with another patch/series (something like [2]) for `net-next` that fixes the
> > ring in a more robust way.
> > 
> > The tx and zc paths seem to have similar low-memory bugs, to be addressed in
> > separate series.
> 
> I've tested this on my Jetson Xavier platform. One of the issues I've
> had is that running iperf3 results in the receive side stalling because
> it runs out of descriptors. However, despite the receive ring
> eventually being re-filled and the hardware appropriately prodded, it
> steadfastly refuses to restart, despite the descriptors having been
> updated.

I'll make it clear: this problem exists without your patches, so it
is not a regression.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* [PATCH] net: lan966x: fix page_pool error handling in lan966x_fdma_rx_alloc_page_pool()
From: David Carlier @ 2026-04-02 17:28 UTC (permalink / raw)
  To: horatiu.vultur, UNGLinuxDriver, andrew+netdev, davem, edumazet,
	kuba, pabeni
  Cc: netdev, linux-kernel, David Carlier, stable

page_pool_create() can return an ERR_PTR on failure. The return value
is used unconditionally in the loop that follows, passing the error
pointer through xdp_rxq_info_reg_mem_model() into page_pool_use_xdp_mem(),
which dereferences it, causing a kernel oops.

Add an IS_ERR check after page_pool_create() to return early on failure.

Fixes: 11871aba1974 ("net: lan96x: Use page_pool API")
Cc: stable@vger.kernel.org
Signed-off-by: David Carlier <devnexen@gmail.com>
---
 drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
index 7b6369e43451..34bbcae2f068 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
@@ -92,6 +92,9 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx)
 
 	rx->page_pool = page_pool_create(&pp_params);
 
+	if (unlikely(IS_ERR(rx->page_pool)))
+		return PTR_ERR(rx->page_pool);
+
 	for (int i = 0; i < lan966x->num_phys_ports; ++i) {
 		struct lan966x_port *port;
 
-- 
2.53.0


^ permalink raw reply related

* Re: [net-next v7 07/10] net: bnxt: Implement software USO
From: Eric Dumazet @ 2026-04-02 17:30 UTC (permalink / raw)
  To: Joe Damato, Eric Dumazet, netdev, Michael Chan, Pavan Chebbi,
	Andrew Lunn, David S. Miller, Jakub Kicinski, Paolo Abeni, horms,
	linux-kernel, leon
In-Reply-To: <ac6dQa1HRrfcpBhX@devvm20253.cco0.facebook.com>

On Thu, Apr 2, 2026 at 9:45 AM Joe Damato <joe@dama.to> wrote:
>
> On Wed, Apr 01, 2026 at 05:35:14PM -0700, Eric Dumazet wrote:
> > On Wed, Apr 1, 2026 at 4:38 PM Joe Damato <joe@dama.to> wrote:
> > >
>
> [...]
>
> > > +       /* Zero the csum fields so tso_build_hdr will propagate zeroes into
> > > +        * every segment header. HW csum offload will recompute from scratch.
> > > +        */
> >
> > We might need a call to skb_cow_head(skb, 0) before changing ->check
> > (or anything in skb->head)
> >
> > Alternative would be to perform the clears after each tso_build_hdr()
> > and leave skb->head untouched.
>
> Thanks for the careful review; I appreciate your time and energy.

Sure thing, very nice work BTW !

>
> I'll remove the existing clears you pointed and perform the clear after each
> tso_build_hdr() as you suggested with something like:
>
> @@ -103,6 +96,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
>                 unsigned int offset;
>                 dma_addr_t dma_addr;
>                 struct tx_bd *txbd;
> +               struct udphdr *uh;
>                 void *this_hdr;
>                 int bd_count;
>                 __le32 csum;
> @@ -116,6 +110,17 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
>
>                 tso_build_hdr(skb, this_hdr, &tso, seg_payload, last);
>
> +               /* Zero stale csum fields copied from the original skb;
> +                * HW offload recomputes from scratch.
> +                */
> +               uh = this_hdr + skb_transport_offset(skb);
> +               uh->check = 0;
> +               if (!tso.ipv6) {
> +                       struct iphdr *iph = this_hdr + skb_network_offset(skb);
> +
> +                       iph->check = 0;
> +               }

This looks good to me, thanks.

^ permalink raw reply

* Re: [GIT PULL] Networking for v7.0-rc7
From: pr-tracker-bot @ 2026-04-02 17:38 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: torvalds, kuba, davem, netdev, linux-kernel, pabeni
In-Reply-To: <20260402162522.666383-1-kuba@kernel.org>

The pull request you sent on Thu,  2 Apr 2026 09:25:22 -0700:

> git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git tags/net-7.0-rc7

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/f8f5627a8aeab15183eef8930bf75ba88a51622f

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* Re: [PATCH net v4 0/2] stmmac crash/stall fixes when under memory pressure
From: Sam Edwards @ 2026-04-02 17:39 UTC (permalink / raw)
  To: Russell King (Oracle)
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maxime Coquelin, Alexandre Torgue, Maxime Chevallier,
	Ovidiu Panait, Vladimir Oltean, Baruch Siach, Serge Semin,
	Giuseppe Cavallaro, netdev, linux-stm32, linux-arm-kernel,
	linux-kernel
In-Reply-To: <ac6kfQ98Xjt3dCGj@shell.armlinux.org.uk>

On Thu, Apr 2, 2026 at 10:16 AM Russell King (Oracle)
<linux@armlinux.org.uk> wrote:
> I've tested this on my Jetson Xavier platform. One of the issues I've
> had is that running iperf3 results in the receive side stalling because
> it runs out of descriptors. However, despite the receive ring
> eventually being re-filled and the hardware appropriately prodded, it
> steadfastly refuses to restart, despite the descriptors having been
> updated.

Hi Russell,

Just to make sure I understand correctly: before my patches, you've
been observing this problem on Xavier for a while (no interrupts, ring
goes dry); with my patches, the ring is refilled, but the dwmac5
doesn't resume DMA. (Ah, just saw your follow-up email.)

> Any ideas?

Off the top of my head, my hypothesis is that dwmac5 has an additional
tripwire when the receive DMA is exhausted, and the
stmmac_set_rx_tail_ptr()/stmmac_enable_dma_reception() at the end of
stmmac_rx_refill() aren't sufficient to wake it back up.

I think this is new to dwmac5, because my RK3588 (dwmac4.20 iirc)
happily resumes after the same condition.

You gave a lot of info; thanks! I'll try to scrape up some
documentation on dwmac5 to see if there's something more
stmmac_rx_refill() ought to be doing. I think I have a Xavier NX
around here somewhere, I'll see if I can repro the problem.

Cheers,
Sam

^ permalink raw reply

* [PATCH net-next] inet: remove leftover EXPORT_SYMBOL()
From: Eric Dumazet @ 2026-04-02 17:44 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Kuniyuki Iwashima, netdev, eric.dumazet,
	Eric Dumazet

IPv6 is no longer a module, we no longer need to export these symbols.

Signed-off-by: Eric Dumazet <edumazet@google.com>
---
 net/ipv4/icmp.c        |  4 ----
 net/ipv4/ipmr_base.c   | 13 -------------
 net/ipv4/udp_offload.c |  4 ----
 3 files changed, 21 deletions(-)

diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index fb397fbb28fc67f012d39398de5e9fdd1dd216bd..2f4fac22d1aba3d100c57a56b51efd57283430a5 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -263,7 +263,6 @@ bool icmp_global_allow(struct net *net)
 	}
 	return true;
 }
-EXPORT_SYMBOL(icmp_global_allow);
 
 void icmp_global_consume(struct net *net)
 {
@@ -273,7 +272,6 @@ void icmp_global_consume(struct net *net)
 	if (credits)
 		atomic_sub(credits, &net->ipv4.icmp_global_credit);
 }
-EXPORT_SYMBOL(icmp_global_consume);
 
 static bool icmpv4_mask_allow(struct net *net, int type, int code)
 {
@@ -1378,7 +1376,6 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
 	icmphdr->code = ICMP_EXT_CODE_MAL_QUERY;
 	return true;
 }
-EXPORT_SYMBOL_GPL(icmp_build_probe);
 
 /*
  *	Handle ICMP Timestamp requests.
@@ -1600,7 +1597,6 @@ void ip_icmp_error_rfc4884(const struct sk_buff *skb,
 	if (!ip_icmp_error_rfc4884_validate(skb, off))
 		out->flags |= SO_EE_RFC4884_FLAG_INVALID;
 }
-EXPORT_SYMBOL_GPL(ip_icmp_error_rfc4884);
 
 int icmp_err(struct sk_buff *skb, u32 info)
 {
diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c
index fd27f2ca69783bc04e76a42c0382b87a64069a07..37a3c144276c75d56d5d295a2c852f5ffd262f65 100644
--- a/net/ipv4/ipmr_base.c
+++ b/net/ipv4/ipmr_base.c
@@ -27,7 +27,6 @@ void vif_device_init(struct vif_device *v,
 	else
 		v->link = dev->ifindex;
 }
-EXPORT_SYMBOL(vif_device_init);
 
 struct mr_table *
 mr_table_alloc(struct net *net, u32 id,
@@ -60,7 +59,6 @@ mr_table_alloc(struct net *net, u32 id,
 	table_set(mrt, net);
 	return mrt;
 }
-EXPORT_SYMBOL(mr_table_alloc);
 
 void *mr_mfc_find_parent(struct mr_table *mrt, void *hasharg, int parent)
 {
@@ -74,7 +72,6 @@ void *mr_mfc_find_parent(struct mr_table *mrt, void *hasharg, int parent)
 
 	return NULL;
 }
-EXPORT_SYMBOL(mr_mfc_find_parent);
 
 void *mr_mfc_find_any_parent(struct mr_table *mrt, int vifi)
 {
@@ -89,7 +86,6 @@ void *mr_mfc_find_any_parent(struct mr_table *mrt, int vifi)
 
 	return NULL;
 }
-EXPORT_SYMBOL(mr_mfc_find_any_parent);
 
 void *mr_mfc_find_any(struct mr_table *mrt, int vifi, void *hasharg)
 {
@@ -109,7 +105,6 @@ void *mr_mfc_find_any(struct mr_table *mrt, int vifi, void *hasharg)
 
 	return mr_mfc_find_any_parent(mrt, vifi);
 }
-EXPORT_SYMBOL(mr_mfc_find_any);
 
 #ifdef CONFIG_PROC_FS
 void *mr_vif_seq_idx(struct net *net, struct mr_vif_iter *iter, loff_t pos)
@@ -124,7 +119,6 @@ void *mr_vif_seq_idx(struct net *net, struct mr_vif_iter *iter, loff_t pos)
 	}
 	return NULL;
 }
-EXPORT_SYMBOL(mr_vif_seq_idx);
 
 void *mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
@@ -143,7 +137,6 @@ void *mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 	}
 	return NULL;
 }
-EXPORT_SYMBOL(mr_vif_seq_next);
 
 void *mr_mfc_seq_idx(struct net *net,
 		     struct mr_mfc_iter *it, loff_t pos)
@@ -168,7 +161,6 @@ void *mr_mfc_seq_idx(struct net *net,
 	it->cache = NULL;
 	return NULL;
 }
-EXPORT_SYMBOL(mr_mfc_seq_idx);
 
 void *mr_mfc_seq_next(struct seq_file *seq, void *v,
 		      loff_t *pos)
@@ -203,7 +195,6 @@ void *mr_mfc_seq_next(struct seq_file *seq, void *v,
 
 	return NULL;
 }
-EXPORT_SYMBOL(mr_mfc_seq_next);
 #endif
 
 int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
@@ -275,7 +266,6 @@ int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
 	rtm->rtm_type = RTN_MULTICAST;
 	return 1;
 }
-EXPORT_SYMBOL(mr_fill_mroute);
 
 static bool mr_mfc_uses_dev(const struct mr_table *mrt,
 			    const struct mr_mfc *c,
@@ -347,7 +337,6 @@ int mr_table_dump(struct mr_table *mrt, struct sk_buff *skb,
 	cb->args[1] = e;
 	return err;
 }
-EXPORT_SYMBOL(mr_table_dump);
 
 int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
 		     struct mr_table *(*iter)(struct net *net,
@@ -390,7 +379,6 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
 
 	return skb->len;
 }
-EXPORT_SYMBOL(mr_rtm_dumproute);
 
 int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family,
 	    int (*rules_dump)(struct net *net,
@@ -444,4 +432,3 @@ int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family,
 
 	return 0;
 }
-EXPORT_SYMBOL(mr_dump);
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 98e92da726b5ec1ce2d12f8de2642f9c11f056a4..a0813d425b7162367866786f1f5d862d1efdcbf5 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -343,7 +343,6 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 
 	return segs;
 }
-EXPORT_SYMBOL(skb_udp_tunnel_segment);
 
 static void __udpv4_gso_segment_csum(struct sk_buff *seg,
 				     __be32 *oldip, __be32 *newip,
@@ -635,7 +634,6 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	}
 	return segs;
 }
-EXPORT_SYMBOL_GPL(__udp_gso_segment);
 
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 					 netdev_features_t features)
@@ -852,7 +850,6 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
 	skb_gro_flush_final(skb, pp, flush);
 	return pp;
 }
-EXPORT_SYMBOL(udp_gro_receive);
 
 static struct sock *udp4_gro_lookup_skb(struct sk_buff *skb, __be16 sport,
 					__be16 dport)
@@ -957,7 +954,6 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
 
 	return err;
 }
-EXPORT_SYMBOL(udp_gro_complete);
 
 INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
 {
-- 
2.53.0.1213.gd9a14994de-goog


^ permalink raw reply related

* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns
From: Nikolaos Gkarlis @ 2026-04-02 17:45 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: netdev, kuniyu
In-Reply-To: <20260401194553.6e8a17f8@kernel.org>

On Thu, Apr 2, 2026 at 4:45 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Sat, 28 Mar 2026 22:33:38 +0100 Nikolaos Gkarlis wrote:
> > -static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
> > +static struct net *rtnl_get_peer_net(struct sk_buff *skb,
> > +                                  const struct rtnl_link_ops *ops,
> >                                    struct nlattr *tbp[],
> >                                    struct nlattr *data[],
> >                                    struct netlink_ext_ack *extack)
> >  {
> >       struct nlattr *tb[IFLA_MAX + 1];
> > +     struct net *net;
> >       int err;
> >
> >       if (!data || !data[ops->peer_type])
>
> There's an early return hiding outside of the context here.
> the patch is technically correct, I think, because if we take this
> shortcut we end up with the same netns as tgt_net so we'll validate
> that it's capable later. But it's probably not obvious to a casual
> reader of this code (or AI agents, sigh)
>
> So let's rewrite this along the lines of:
>
>         struct nlattr *tb[IFLA_MAX + 1], **attrs;
>         struct net *net;
>         int err;
>
>         if (!data || !data[ops->peer_type]) {
>                attrs = tbp;
>         } else {
>                 err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack);
>                 if (err < 0)
>                         return ERR_PTR(err);
>
>                 if (ops->validate) {
>                         err = ops->validate(tb, NULL, extack);
>                         if (err < 0)
>                                 return ERR_PTR(err);
>                 }
>
>                 attrs = tb;
>         }
>
>         net = rtnl_link_get_net_ifla(attrs);
>         if (IS_ERR_OR_NULL(net))
>                 return net;
>
>         if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
> ...
>
> ?

I agree it’s a bit confusing with the early exit. I’ll apply the suggested
changes and send a v5.

That said, would it make sense to introduce a separate
rtnl_nets_add_capable() helper that wraps rtnl_nets_add() instead?

Something along these lines:

@@ -334,6 +334,19 @@ static void rtnl_nets_add(struct rtnl_nets
*rtnl_nets, struct net *net)
     rtnl_nets->len++;
 }

+static struct net *rtnl_nets_add_capable(struct sk_buff *skb,
+                                         struct rtnl_nets *rtnl_nets,
+                                         struct net *net)
+{
+    if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+        put_net(net);
+        return ERR_PTR(-EPERM);
+    }
+
+    rtnl_nets_add(rtnl_nets, net);
+    return net;
+}
+
 static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets)
 {
     int i;
@@ -4059,18 +4072,29 @@ static int rtnl_newlink(struct sk_buff *skb,
struct nlmsghdr *nlh,
         ret = PTR_ERR(peer_net);
         goto put_ops;
     }
-    if (peer_net)
-        rtnl_nets_add(&rtnl_nets, peer_net);
+    if (peer_net) {
+        peer_net = rtnl_nets_add_capable(skb,
+                                        &rtnl_nets,
+                                        peer_net);
+        if (IS_ERR(peer_net)) {
+            ret = PTR_ERR(peer_net);
+            goto put_ops;
+        }
+    }
     }
     }

-    tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb,
CAP_NET_ADMIN);
+   tgt_net = rtnl_link_get_net_by_nlattr(sock_net(skb->sk), tb);
     if (IS_ERR(tgt_net)) {
         ret = PTR_ERR(tgt_net);
         goto put_net;
     }

-    rtnl_nets_add(&rtnl_nets, tgt_net);
+    tgt_net = rtnl_nets_add_capable(skb, &rtnl_nets, tgt_net);
+    if (IS_ERR(tgt_net)) {
+        ret = PTR_ERR(tgt_net);
+        goto put_net;
+    }

     if (tb[IFLA_LINK_NETNSID]) {
         int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
@@ -4082,10 +4106,10 @@ static int rtnl_newlink(struct sk_buff *skb,
struct nlmsghdr *nlh,
         goto put_net;
     }

-    rtnl_nets_add(&rtnl_nets, link_net);
-
-    if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) {
-        ret = -EPERM;
+    link_net = rtnl_nets_add_capable(skb, &rtnl_nets,
+                                    link_net);
+    if (IS_ERR(link_net)) {
+        ret = PTR_ERR(link_net);
         goto put_net;
     }
     }

Note that this changes the order in link_net, checking capabilities
before creating the object.

Disclaimer: I’m not very familiar with this code, so this may be a bad idea.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox