* Re: [PATCH net v2] ipv6: addrconf: skip ERRDAD transition when address already DEAD
From: Sabrina Dubroca @ 2026-04-22 10:43 UTC (permalink / raw)
To: Linmao Li
Cc: davem, dsahern, edumazet, kuba, pabeni, horms, netdev,
linux-kernel
In-Reply-To: <20260421075033.1110816-1-lilinmao@kylinos.cn>
2026-04-21, 15:50:33 +0800, Linmao Li wrote:
> addrconf_dad_end() transitions ifp->state from DAD to POSTDAD under
> ifp->lock and releases the lock. addrconf_dad_failure() takes
> ifp->lock again with the spin_lock_bh() following the
> net_info_ratelimited() duplicate-address log. A concurrent
> ipv6_del_addr() can acquire the lock in that window, set ifp->state
> to DEAD and run list_del_rcu(&ifp->if_list).
You're pretty much saying that the ifp->state check we did in
addrconf_dad_end before dropping the lock is not valid, so it seems we
should just skip that separate check since it's not doing anything
useful, and move it under the "main" lock we acquire after the
net_info_ratelimited(). There would still be a problem with "we
dropped the lock in the STABLE_PRIVACY block", which your patch
handles.
> addrconf_dad_failure() then overwrites DEAD with ERRDAD at errdad:
> and schedules a new dad_work. The work calls ipv6_del_addr() again,
> hitting the already-poisoned list entry:
>
> general protection fault: 0000 [#1] SMP NOPTI
> CPU: 4 PID: 217 Comm: kworker/4:1
> Workqueue: ipv6_addrconf addrconf_dad_work
> RIP: 0010:ipv6_del_addr+0xe9/0x280
> RAX: dead000000000122
> Call Trace:
> addrconf_dad_stop+0x113/0x140
> addrconf_dad_work+0x28c/0x430
> process_one_work+0x1eb/0x3b0
> worker_thread+0x4d/0x400
> kthread+0x104/0x140
> ret_from_fork+0x35/0x40
>
> Bail out at errdad: when ifp->state is already DEAD. The existing
> in6_ifa_put() releases the reference taken for this invocation.
Mentioning "the existing in6_ifa_put()" is a bit confusing since
you're adding a separate unlock/put/return path.
--
Sabrina
^ permalink raw reply
* Re: [PATCH net 00/18] Remove a number of ISA and PCMCIA Ethernet drivers
From: Finn Thain @ 2026-04-22 10:45 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch>
On Tue, 21 Apr 2026, Andrew Lunn wrote:
> These old drivers have not been much of a Maintenance burden until
> recently.
They are not much of a maintenance burden because they are small, mature
and stable.
> Now there are more newbies using AI and fuzzers finding issues,
> resulting in more work for Maintainers.
AI helps find issues but AI also helps resolve issues.
> Fixing these old drivers make little sense, if it is not clear they have
> users.
>
Removing these old drivers makes little sense, if the issues were only
noticed by AI and not by actual users.
^ permalink raw reply
* Re: [PATCH net-deletions] net: remove ax25 and amateur radio (hamradio) subsystem
From: Simon Horman @ 2026-04-22 10:45 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, corbet, skhan,
federico.vaga, carlos.bilbao, avadhut.naik, alexs, si.yanteng,
dzm91, 2023002089, tsbogend, dsahern, jani.nikula, mchehab+huawei,
gregkh, jirislaby, tytso, herbert, ebiggers, johannes.berg, geert,
pablo, tglx, mashiro.chen, mingo, dqfext, jreuter, sdf, pkshih,
enelsonmoore, mkl, toke, kees, crossd, jlayton, wangliang74,
aha310510, takamitz, kuniyu, linux-doc, linux-mips
In-Reply-To: <20260421021824.1293976-1-kuba@kernel.org>
On Mon, Apr 20, 2026 at 07:18:23PM -0700, Jakub Kicinski wrote:
> Remove the amateur radio (AX.25, NET/ROM, ROSE) protocol implementation
> and all associated hamradio device drivers from the kernel tree.
> This set of protocols has long been a huge bug/syzbot magnet,
> and since nobody stepped up to help us deal with the influx
> of the AI-generated bug reports we need to move it out of tree
> to protect our sanity.
>
> The code is moved to an out-of-tree repo:
> https://github.com/linux-netdev/mod-orphan
> if it's cleaned up and reworked there we can accept it back.
>
> Minimal stub headers are kept for include/net/ax25.h (AX25_P_IP,
> AX25_ADDR_LEN, ax25_address) and include/net/rose.h (ROSE_ADDR_LEN)
> so that the conditional integration code in arp.c and tun.c continues
> to compile and work when the out-of-tree modules are loaded.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH net-deletions] caif: remove CAIF NETWORK LAYER
From: Simon Horman @ 2026-04-22 10:48 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, corbet, skhan,
alexs, si.yanteng, dzm91, linux, mst, jasowang, xuanzhuo,
eperezma, xu.xin16, wang.yaxin, jiang.kun2, linusw,
jihed.chaibi.dev, arnd, tytso, jiayuan.chen, gregkh
In-Reply-To: <20260416182829.1440262-1-kuba@kernel.org>
On Thu, Apr 16, 2026 at 11:28:28AM -0700, Jakub Kicinski wrote:
> Remove CAIF (Communication CPU to Application CPU Interface), the
> ST-Ericsson modem protocol. The subsystem has been orphaned since 2013.
> The last meaningful changes from the maintainers were in March 2013:
> a8c7687bf216 ("caif_virtio: Check that vringh_config is not null")
> b2273be8d2df ("caif_virtio: Use vringh_notify_enable correctly")
> 0d2e1a2926b1 ("caif_virtio: Introduce caif over virtio")
>
> Not-so-coincidentally, according to "the Internet" ST-Ericsson officially
> shut down its modem joint venture in Aug 2013.
>
> If anyone is using this code please yell!
>
> In the 13 years since, the code has accumulated 200 non-merge commits,
> of which 71 were cross-tree API changes, 21 carried Fixes: tags, and
> the remaining ~110 were cleanups, doc conversions, treewide refactors,
> and one partial removal (caif_hsi, ca75bcf0a83b).
>
> We are still getting fixes to this code, in the last 10 days there were
> 3 reports on security@ about CAIF that I have been CCed on.
>
> UAPI constants (AF_CAIF, ARPHRD_CAIF, N_CAIF, VIRTIO_ID_CAIF) and the
> SELinux classmap entry are intentionally kept for ABI stability.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> I think we should accumulate such patches over the coming days on a separate
> branch. CAIF is a no-brainer IMO but other removals may be more controversial.
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH net-deletions v2] net: remove unused ATM protocols and legacy ATM device drivers
From: Simon Horman @ 2026-04-22 10:53 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, corbet, skhan,
linux, tsbogend, maddy, mpe, npiggin, chleroy, 3chas3, razor,
idosch, jani.nikula, mchehab+huawei, tytso, herbert, geert,
ebiggers, johannes.berg, jonathan.cameron, kees, kuniyu,
fourier.thomas, andriy.shevchenko, rdunlap, akpm, linux-doc,
linux-mips, linuxppc-dev, bridge, dwmw2
In-Reply-To: <20260422041846.2035118-1-kuba@kernel.org>
On Tue, Apr 21, 2026 at 09:18:44PM -0700, Jakub Kicinski wrote:
> Remove the ATM protocol modules and PCI/SBUS ATM device drivers
> that are no longer in active use.
>
> The ATM core protocol stack, PPPoATM, BR2684, and USB DSL modem
> drivers (drivers/usb/atm/) are retained in-tree to maintain PPP
> over ATM (PPPoA) and PPPoE-over-BR2684 support for DSL connections.
>
> Removed ATM protocol modules:
> - net/atm/clip.c - Classical IP over ATM (RFC 2225)
> - net/atm/lec.c - LAN Emulation Client (LANE)
> - net/atm/mpc.c, mpoa_caches.c, mpoa_proc.c - Multi-Protocol Over ATM
>
> Removed PCI/SBUS ATM device drivers (drivers/atm/):
> - adummy, atmtcp - software/testing ATM devices
> - eni - Efficient Networks ENI155P (OC-3, ~1995)
> - fore200e - FORE Systems 200E PCI/SBUS (OC-3, ~1999)
> - he - ForeRunner HE (OC-3/OC-12, ~2000)
> - idt77105 - IDT 77105 25 Mbps ATM PHY
> - idt77252 - IDT 77252 NICStAR II (OC-3, ~2000)
> - iphase - Interphase ATM PCI (OC-3/DS3/E3)
> - lanai - Efficient Networks Speedstream 3010
> - nicstar - IDT 77201 NICStAR (155/25 Mbps, ~1999)
> - solos-pci - Traverse Technologies ADSL2+ PCI
> - suni - PMC S/UNI SONET PHY library
>
> Also clean up references in:
> - net/bridge/ - remove ATM LANE hook (br_fdb_test_addr_hook,
> br_fdb_test_addr)
> - net/core/dev.c - remove br_fdb_test_addr_hook export
> - defconfig files - remove ATM driver config options
>
> The removed code is moved to an out-of-tree module package (mod-orphan).
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> v2:
> - keep BR2684
> - correct the claim that Traverse Technologies is defunct,
> I'm still deleting the solos driver, chances are nobody uses it.
> Easy enough to revert back in since core is still around.
> The guiding principle is to keep USB modems and delete
> the rest as USB ADSL2+ CPEs were most popular historically.
> v1: https://lore.kernel.org/20260421021943.1295109-1-kuba@kernel.org
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH net-deletions] net: remove ISDN subsystem and Bluetooth CMTP
From: Simon Horman @ 2026-04-22 10:55 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, corbet, skhan,
marcel, luiz.dentz, mchehab+huawei, jani.nikula, gregkh, demarchi,
rdunlap, justonli, ivecera, jonathan.cameron, kees,
marco.crivellari, ferr.lambarginio, nihaal, mingo, tglx, linmq006,
linux-doc, linux-bluetooth
In-Reply-To: <20260421022108.1299678-1-kuba@kernel.org>
On Mon, Apr 20, 2026 at 07:21:07PM -0700, Jakub Kicinski wrote:
> Remove the ISDN (mISDN, CAPI) subsystem and Bluetooth CMTP protocol
> from the kernel tree.
>
> ISDN is a pretty old technology and it's unclear whether anyone still
> uses it. I went over the last few years of git history and all the
> commits are either tree-wide conversions or syzbot/static analyzer
> fixes.
>
> When we discussed removal in the past IIRC there were some concerns
> about ISDN still being used in parts of Germany. Unfortunately, the
> code base is quite old, none of the current maintainers are familiar
> with it and AI tools will have a field day finding bugs here.
>
> Delete this code and preserve it in an out-of-tree repository
> for any remaining users:
> https://github.com/linux-netdev/mod-orphan
>
> UAPI constants AF_ISDN/PF_ISDN and the SELinux isdn_socket class
> are preserved for ABI stability, but the rest of uAPI is removed.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* [PATCH v2] net/intel: Replace manual array size calculation with ARRAY_SIZE
From: Jakub Raczynski @ 2026-04-22 10:57 UTC (permalink / raw)
To: error27
Cc: netdev, kuba, przemyslaw.kitszel, anthony.l.nguyen,
kernel-janitors, Jakub Raczynski
In-Reply-To: <aeeFh1zQqhVysvxI@stanley.mountain>
There are still places in the code where manual calculation of array size
exist, but it is good to enforce usage of single macro through the whole
code as it makes code bit more readable.
While at it, beautify condition surrounding it by reversing check and remove
unnecessary casting.
Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
---
drivers/net/ethernet/intel/i40e/i40e_adminq.h | 2 +-
drivers/net/ethernet/intel/iavf/iavf_adminq.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h
index 1be97a3a86ce..dcf3baec7b73 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h
@@ -109,7 +109,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc)
-EFBIG, /* I40E_AQ_RC_EFBIG */
};
- if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0]))))
+ if (aq_rc >= ARRAY_SIZE(aq_to_posix))
return -ERANGE;
return aq_to_posix[aq_rc];
diff --git a/drivers/net/ethernet/intel/iavf/iavf_adminq.h b/drivers/net/ethernet/intel/iavf/iavf_adminq.h
index bbf5c4b3a2ae..dd2f61172157 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_adminq.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_adminq.h
@@ -113,7 +113,7 @@ static inline int iavf_aq_rc_to_posix(int aq_ret, int aq_rc)
if (aq_ret == IAVF_ERR_ADMIN_QUEUE_TIMEOUT)
return -EAGAIN;
- if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0]))))
+ if (aq_rc >= ARRAY_SIZE(aq_to_posix))
return -ERANGE;
return aq_to_posix[aq_rc];
--
2.34.1
^ permalink raw reply related
* [PATCH] ipv6: udp: fix memory leak in udpv6_sendmsg error path
From: Mingyu Wang @ 2026-04-22 10:58 UTC (permalink / raw)
To: willemdebruijn.kernel, davem, dsahern, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, Mingyu Wang
During fuzzing with failslab enabled, a memory leak was observed in the
IPv6 UDP send path.
When sending via the lockless fast path (!corkreq), udpv6_sendmsg()
calls ip6_make_skb() and assumes that the routing entry (dst_entry)
reference has been stolen by the callee. However, if ip6_make_skb()
fails early (e.g., due to an ENOMEM from memory allocation failure),
it returns an error pointer without consuming the dst reference.
Since udpv6_sendmsg() unconditionally jumps to the 'out_no_dst' label,
the unconsumed dst_entry is never released, resulting in a memory leak.
Fix this by explicitly calling dst_release(dst) when ip6_make_skb()
returns an error.
Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
net/ipv6/udp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194ecc..b83ecfd729af 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1706,8 +1706,11 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
dst_rt6_info(dst),
msg->msg_flags, &cork);
err = PTR_ERR(skb);
- if (!IS_ERR_OR_NULL(skb))
+ if (!IS_ERR_OR_NULL(skb)) {
err = udp_v6_send_skb(skb, fl6, &cork.base);
+ } else {
+ dst_release(dst);
+ }
/* ip6_make_skb steals dst reference */
goto out_no_dst;
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net v3 1/1] net: hsr: limit node table growth
From: Sebastian Andrzej Siewior @ 2026-04-22 10:58 UTC (permalink / raw)
To: Felix Maurer
Cc: Ren Wei, netdev, davem, edumazet, kuba, pabeni, horms, kees,
kexinsun, luka.gejak, Arvid.Brodin, m-karicheri2, yuantan098,
yifanwucs, tomapufckgml, bird, xuyuqiabc, royenheart
In-Reply-To: <aeiYpkiWUD5MtGEB@thinkpad>
On 2026-04-22 11:45:38 [+0200], Felix Maurer wrote:
> > I don't think the node count exceeds 100 in production. So having a
> > counter which is incremented while adding to the list and decremented
> > while removing items from the list would optimize the "worst case". So
> > instead traversing the list with 1000 we would just give up.
>
> The counter is what I had in mind. I agree that allocating under the
> lock isn't what we want.
>
> I'd argue counting through the whole list is the normal case.
yeah but counting here is just a register increment which is cheap.
> hsr_add_node() is only called after the node table has been searched
> already (without the lock). Here we go through the whole list again
> under the lock to prevent TOCTOU-type situations.
>
> I agree that, overall, it would be optimizing the worst case, but I
> think it may be worth it to prevent the memory allocations and walking
> the whole list. But I'd go along with the (current) on-the-fly counting
> as well.
Yeah. But then you have to manage the counter on add and removal just
for this "we have too many nodes" case. And theoretically you would have
to hold the list_lock while checking the counter because nodes might be
added on both sides in the RX path (unless you check early lockless &
optimistic and then again before adding under the lock).
So overall this looks simpler.
> Thanks,
> Felix
Sebastian
^ permalink raw reply
* Re: [syzbot] [net?] kernel BUG in pn_socket_autobind
From: syzbot @ 2026-04-22 11:16 UTC (permalink / raw)
To: courmisch, davem, edumazet, horms, kuba, linux-kernel, netdev,
pabeni, syzkaller-bugs
In-Reply-To: <69e79944.a00a0220.17a17.001a.GAE@google.com>
syzbot has found a reproducer for the following issue on:
HEAD commit: 6596a02b2078 Merge tag 'drm-next-2026-04-22' of https://gi..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=15ca74ce580000
kernel config: https://syzkaller.appspot.com/x/.config?x=3e19fa1907a3dfda
dashboard link: https://syzkaller.appspot.com/bug?extid=b3c0e6a240078433c42b
compiler: gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=17330cce580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=107ed1ba580000
Downloadable assets:
disk image (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-6596a02b.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/1e78cc4623b5/vmlinux-6596a02b.xz
kernel image: https://storage.googleapis.com/syzbot-assets/7ae78b19bafa/bzImage-6596a02b.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+b3c0e6a240078433c42b@syzkaller.appspotmail.com
------------[ cut here ]------------
kernel BUG at net/phonet/socket.c:213!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 6116 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:pn_socket_autobind net/phonet/socket.c:213 [inline]
RIP: 0010:pn_socket_autobind+0x14c/0x170 net/phonet/socket.c:202
Code: 00 00 00 00 48 8b 44 24 58 65 48 2b 05 55 4d 47 09 75 2a 48 83 c4 60 89 d8 5b 5d 41 5c 41 5d e9 1a f8 d0 00 e8 15 78 3c f7 90 <0f> 0b e8 cd 30 aa f7 eb 9e e8 26 31 aa f7 e9 6c ff ff ff e8 7c b8
RSP: 0018:ffffc90003d5fa30 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff8acd028a
RDX: ffff88802b48ca00 RSI: ffffffff8acd02cb RDI: ffff88802b48ca00
RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 1ffff920007abf46
R13: dffffc0000000000 R14: 1ffff920007abf61 R15: ffffc90003d5fe48
FS: 00007f523dc816c0(0000) GS:ffff8880d62db000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fdf9b231e9c CR3: 000000004a755000 CR4: 0000000000352ef0
Call Trace:
<TASK>
pn_socket_sendmsg+0x43/0xe0 net/phonet/socket.c:421
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x9e1/0xb70 net/socket.c:2698
___sys_sendmsg+0x190/0x1e0 net/socket.c:2752
__sys_sendmsg+0x170/0x220 net/socket.c:2784
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f523cd9c819
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f523dc81028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f523d015fa0 RCX: 00007f523cd9c819
RDX: 0000000004044004 RSI: 0000200000000200 RDI: 000000000000000a
RBP: 00007f523ce32c91 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f523d016038 R14: 00007f523d015fa0 R15: 00007ffe40f56cd8
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:pn_socket_autobind net/phonet/socket.c:213 [inline]
RIP: 0010:pn_socket_autobind+0x14c/0x170 net/phonet/socket.c:202
Code: 00 00 00 00 48 8b 44 24 58 65 48 2b 05 55 4d 47 09 75 2a 48 83 c4 60 89 d8 5b 5d 41 5c 41 5d e9 1a f8 d0 00 e8 15 78 3c f7 90 <0f> 0b e8 cd 30 aa f7 eb 9e e8 26 31 aa f7 e9 6c ff ff ff e8 7c b8
RSP: 0018:ffffc90003d5fa30 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff8acd028a
RDX: ffff88802b48ca00 RSI: ffffffff8acd02cb RDI: ffff88802b48ca00
RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 1ffff920007abf46
R13: dffffc0000000000 R14: 1ffff920007abf61 R15: ffffc90003d5fe48
FS: 00007f523dc816c0(0000) GS:ffff8880d62db000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fdf9b231e9c CR3: 000000004a755000 CR4: 0000000000352ef0
---
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
^ permalink raw reply
* [PATCH 0/5] io_uring/zcrx: add CQE based notifications and stats reporting
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
The zcrx path can encounter various conditions that lead to internal
fallbacks or errors. These errors can have a large impact on performance
and functionality but are not yet not being reported to the user which
is then unable to take action.
This series addresses this problem by adding a new notification system
paired with a statistics structure. The notification system currently
report out of buffer and packets that fallback to copy. The statistics
structure report the number and total size of packets that were copied
rather than received via the zero-copy path.
The out of buffer notification allows the user to actually adjust the
buffer sizing when registering zcrx support for the ifq. Some future
work could allow the user to add more memory on the fly to the pool so
the page allocator doesn't run out of memory.
This series can be tested using the include kselftest modification and
using the liburing series that updates headers and tests/examples so
that it uses notifications and statistics.
Clément Léger (4):
io_uring/zcrx: notify user on frag copy fallback
io_uring/zcrx: add shared-memory notification statistics
Documentation: networking: document zcrx notifications and statistics
selftests: iou-zcrx: add notification and stats test for zcrx
Pavel Begunkov (1):
io_uring/zcrx: notify user when out of buffers
Documentation/networking/iou-zcrx.rst | 106 ++++++++++++
include/uapi/linux/io_uring/query.h | 12 ++
include/uapi/linux/io_uring/zcrx.h | 34 +++-
io_uring/query.c | 14 ++
io_uring/zcrx.c | 151 +++++++++++++++++-
io_uring/zcrx.h | 13 +-
.../selftests/drivers/net/hw/iou-zcrx.c | 112 +++++++++++--
.../selftests/drivers/net/hw/iou-zcrx.py | 49 +++++-
8 files changed, 475 insertions(+), 16 deletions(-)
--
Clément Léger
^ permalink raw reply
* [PATCH 1/5] io_uring/zcrx: notify user when out of buffers
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: linux-doc, linux-kernel, linux-kselftest, netdev, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan, Vishwanath Seshagiri,
Vishwanath Seshagiri
In-Reply-To: <20260422112522.3316660-1-cleger@meta.com>
From: Pavel Begunkov <asml.silence@gmail.com>
There are currently no easy ways for the user to know if zcrx is out of
buffers and page pool fails to allocate. Add uapi for zcrx to communicate
it back.
It's implemented as a separate CQE, which for now is posted to the creator
ctx. To use it, on registration the user space needs to pass an instance
of struct zcrx_notification_desc, which tells the kernel the user_data
for resulting CQEs and which event types are expected / allowed.
When an allowed event happens, zcrx will post a CQE containing the
specified user_data, and lower bits of cqe->res will be set to the event
mask. Before the kernel could post another notification of the given
type, the user needs to acknowledge that it processed the previous one
by issuing IORING_REGISTER_ZCRX_CTRL with ZCRX_CTRL_ARM_NOTIFICATION.
The only notification type the patch implements yet is
ZCRX_NOTIF_NO_BUFFERS. Next commit adds copy fallback signaling.
Co-developed-by: Vishwanath Seshagiri <vishs@meta.com>
Signed-off-by: Vishwanath Seshagiri <vishs@meta.com>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/zcrx.h | 22 ++++++-
io_uring/zcrx.c | 98 +++++++++++++++++++++++++++++-
io_uring/zcrx.h | 11 +++-
3 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index 5ce02c7a6096..b8596d7d47b6 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -65,6 +65,18 @@ enum zcrx_features {
* value in struct io_uring_zcrx_ifq_reg::rx_buf_len.
*/
ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0,
+ ZCRX_FEATURE_NOTIFICATION = 1 << 1,
+};
+
+enum zcrx_notification_type {
+ ZCRX_NOTIF_NO_BUFFERS = 1 << 0,
+};
+
+struct zcrx_notification_desc {
+ __u64 user_data;
+ __u32 type_mask;
+ __u32 __resv1;
+ __u64 __resv2[10];
};
/*
@@ -82,12 +94,14 @@ struct io_uring_zcrx_ifq_reg {
struct io_uring_zcrx_offsets offsets;
__u32 zcrx_id;
__u32 rx_buf_len;
- __u64 __resv[3];
+ __u64 notif_desc; /* see struct zcrx_notification_desc */
+ __u64 __resv[2];
};
enum zcrx_ctrl_op {
ZCRX_CTRL_FLUSH_RQ,
ZCRX_CTRL_EXPORT,
+ ZCRX_CTRL_ARM_NOTIFICATION,
__ZCRX_CTRL_LAST,
};
@@ -101,6 +115,11 @@ struct zcrx_ctrl_export {
__u32 __resv1[11];
};
+struct zcrx_ctrl_arm_notif {
+ __u32 type_mask;
+ __u32 __resv[11];
+};
+
struct zcrx_ctrl {
__u32 zcrx_id;
__u32 op; /* see enum zcrx_ctrl_op */
@@ -109,6 +128,7 @@ struct zcrx_ctrl {
union {
struct zcrx_ctrl_export zc_export;
struct zcrx_ctrl_flush_rq zc_flush;
+ struct zcrx_ctrl_arm_notif zc_arm_notif;
};
};
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 9a83d7eb4210..35ca28cb6583 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -44,6 +44,16 @@ static inline struct io_zcrx_area *io_zcrx_iov_to_area(const struct net_iov *nio
return container_of(owner, struct io_zcrx_area, nia);
}
+static bool zcrx_set_ring_ctx(struct io_zcrx_ifq *zcrx, struct io_ring_ctx *ctx)
+{
+ guard(spinlock_bh)(&zcrx->ctx_lock);
+ if (zcrx->master_ctx)
+ return false;
+ percpu_ref_get(&ctx->refs);
+ zcrx->master_ctx = ctx;
+ return true;
+}
+
static inline struct page *io_zcrx_iov_page(const struct net_iov *niov)
{
struct io_zcrx_area *area = io_zcrx_iov_to_area(niov);
@@ -531,6 +541,7 @@ static struct io_zcrx_ifq *io_zcrx_ifq_alloc(struct io_ring_ctx *ctx)
ifq->if_rxq = -1;
spin_lock_init(&ifq->rq.lock);
+ spin_lock_init(&ifq->ctx_lock);
mutex_init(&ifq->pp_lock);
refcount_set(&ifq->refs, 1);
refcount_set(&ifq->user_refs, 1);
@@ -585,6 +596,11 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq)
if (ifq->dev)
put_device(ifq->dev);
+ scoped_guard(spinlock_bh, &ifq->ctx_lock) {
+ if (ifq->master_ctx)
+ percpu_ref_put(&ifq->master_ctx->refs);
+ }
+
io_free_rbuf_ring(ifq);
mutex_destroy(&ifq->pp_lock);
kfree(ifq);
@@ -738,6 +754,8 @@ static int import_zcrx(struct io_ring_ctx *ctx,
return -EINVAL;
if (reg->if_rxq || reg->rq_entries || reg->area_ptr || reg->region_ptr)
return -EINVAL;
+ if (reg->notif_desc)
+ return -EINVAL;
if (reg->flags & ~ZCRX_REG_IMPORT)
return -EINVAL;
@@ -826,6 +844,7 @@ static int zcrx_register_netdev(struct io_zcrx_ifq *ifq,
int io_register_zcrx(struct io_ring_ctx *ctx,
struct io_uring_zcrx_ifq_reg __user *arg)
{
+ struct zcrx_notification_desc notif;
struct io_uring_zcrx_area_reg area;
struct io_uring_zcrx_ifq_reg reg;
struct io_uring_region_desc rd;
@@ -869,10 +888,22 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
if (copy_from_user(&area, u64_to_user_ptr(reg.area_ptr), sizeof(area)))
return -EFAULT;
+ memset(¬if, 0, sizeof(notif));
+ if (reg.notif_desc && copy_from_user(¬if, u64_to_user_ptr(reg.notif_desc),
+ sizeof(notif)))
+ return -EFAULT;
+ if (notif.type_mask & ~ZCRX_NOTIF_TYPE_MASK)
+ return -EINVAL;
+ if (notif.__resv1 || !mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
+ return -EINVAL;
+
ifq = io_zcrx_ifq_alloc(ctx);
if (!ifq)
return -ENOMEM;
+ ifq->notif_data = notif.user_data;
+ ifq->allowed_notif_mask = notif.type_mask;
+
if (ctx->user) {
get_uid(ctx->user);
ifq->user = ctx->user;
@@ -923,6 +954,9 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
ret = -EFAULT;
goto err;
}
+
+ if (notif.type_mask)
+ zcrx_set_ring_ctx(ifq, ctx);
return 0;
err:
scoped_guard(mutex, &ctx->mmap_lock)
@@ -1089,6 +1123,46 @@ static unsigned io_zcrx_refill_slow(struct page_pool *pp, struct io_zcrx_ifq *if
return allocated;
}
+static void zcrx_notif_tw(struct io_tw_req tw_req, io_tw_token_t tw)
+{
+ struct io_kiocb *req = tw_req.req;
+ struct io_ring_ctx *ctx = req->ctx;
+
+ io_post_aux_cqe(ctx, req->cqe.user_data, req->cqe.res, 0);
+ percpu_ref_put(&ctx->refs);
+ kfree_rcu(req, rcu_head);
+}
+
+static void zcrx_send_notif(struct io_zcrx_ifq *ifq, u32 type_mask)
+{
+ gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO;
+ struct io_kiocb *req;
+
+ if (!(type_mask & ifq->allowed_notif_mask))
+ return;
+
+ guard(spinlock_bh)(&ifq->ctx_lock);
+ if (!ifq->master_ctx)
+ return;
+ if (type_mask & ifq->fired_notifs)
+ return;
+
+ req = kmem_cache_alloc(req_cachep, gfp);
+ if (unlikely(!req))
+ return;
+
+ ifq->fired_notifs |= type_mask;
+
+ req->opcode = IORING_OP_NOP;
+ req->cqe.user_data = ifq->notif_data;
+ req->cqe.res = type_mask;
+ req->ctx = ifq->master_ctx;
+ percpu_ref_get(&req->ctx->refs);
+ req->tctx = NULL;
+ req->io_task_work.func = zcrx_notif_tw;
+ io_req_task_work_add(req);
+}
+
static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp)
{
struct io_zcrx_ifq *ifq = io_pp_to_ifq(pp);
@@ -1105,8 +1179,10 @@ static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp)
goto out_return;
allocated = io_zcrx_refill_slow(pp, ifq, netmems, to_alloc);
- if (!allocated)
+ if (!allocated) {
+ zcrx_send_notif(ifq, ZCRX_NOTIF_NO_BUFFERS);
return 0;
+ }
out_return:
zcrx_sync_for_device(pp, ifq, netmems, allocated);
allocated--;
@@ -1255,12 +1331,30 @@ static int zcrx_flush_rq(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx,
return 0;
}
+static int zcrx_arm_notif(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx,
+ struct zcrx_ctrl *ctrl)
+{
+ const struct zcrx_ctrl_arm_notif *an = &ctrl->zc_arm_notif;
+
+ if (an->type_mask & ~ZCRX_NOTIF_TYPE_MASK)
+ return -EINVAL;
+ if (!mem_is_zero(&an->__resv, sizeof(an->__resv)))
+ return -EINVAL;
+
+ guard(spinlock_bh)(&zcrx->ctx_lock);
+ if (an->type_mask & ~zcrx->fired_notifs)
+ return -EINVAL;
+ zcrx->fired_notifs &= ~an->type_mask;
+ return 0;
+}
+
int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
{
struct zcrx_ctrl ctrl;
struct io_zcrx_ifq *zcrx;
BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_flush));
+ BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_arm_notif));
if (nr_args)
return -EINVAL;
@@ -1278,6 +1372,8 @@ int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args)
return zcrx_flush_rq(ctx, zcrx, &ctrl);
case ZCRX_CTRL_EXPORT:
return zcrx_export(ctx, zcrx, &ctrl, arg);
+ case ZCRX_CTRL_ARM_NOTIFICATION:
+ return zcrx_arm_notif(ctx, zcrx, &ctrl);
}
return -EOPNOTSUPP;
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 75e0a4e6ef6e..3ddebed06d57 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -9,7 +9,9 @@
#include <net/net_trackers.h>
#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV)
-#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE)
+#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\
+ ZCRX_FEATURE_NOTIFICATION)
+#define ZCRX_NOTIF_TYPE_MASK (ZCRX_NOTIF_NO_BUFFERS)
struct io_zcrx_mem {
unsigned long size;
@@ -72,6 +74,13 @@ struct io_zcrx_ifq {
*/
struct mutex pp_lock;
struct io_mapped_region rq_region;
+
+ /* Locks the access to notifification context data */
+ spinlock_t ctx_lock;
+ struct io_ring_ctx *master_ctx;
+ u32 allowed_notif_mask;
+ u32 fired_notifs;
+ u64 notif_data;
};
#if defined(CONFIG_IO_URING_ZCRX)
--
2.52.0
^ permalink raw reply related
* [PATCH 2/5] io_uring/zcrx: notify user on frag copy fallback
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260422112522.3316660-1-cleger@meta.com>
Add a ZCRX_NOTIF_COPY notification type to signal userspace when a
received fragment could not be delivered using zero-copy and was
instead copied into a buffer.
Signed-off-by: Clément Léger <cleger@meta.com>
---
include/uapi/linux/io_uring/zcrx.h | 1 +
io_uring/zcrx.c | 7 ++++++-
io_uring/zcrx.h | 3 ++-
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index b8596d7d47b6..e0c0079626c8 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -70,6 +70,7 @@ enum zcrx_features {
enum zcrx_notification_type {
ZCRX_NOTIF_NO_BUFFERS = 1 << 0,
+ ZCRX_NOTIF_COPY = 1 << 1
};
struct zcrx_notification_desc {
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 35ca28cb6583..732e585aa13a 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -1510,8 +1510,13 @@ static int io_zcrx_copy_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
const skb_frag_t *frag, int off, int len)
{
struct page *page = skb_frag_page(frag);
+ int ret;
+
+ ret = io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
+ if (ret > 0)
+ zcrx_send_notif(ifq, ZCRX_NOTIF_COPY);
- return io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
+ return ret;
}
static int io_zcrx_recv_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 3ddebed06d57..1bd63adaa711 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -11,7 +11,8 @@
#define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV)
#define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\
ZCRX_FEATURE_NOTIFICATION)
-#define ZCRX_NOTIF_TYPE_MASK (ZCRX_NOTIF_NO_BUFFERS)
+#define ZCRX_NOTIF_TYPE_MASK (ZCRX_NOTIF_NO_BUFFERS |\
+ ZCRX_NOTIF_COPY)
struct io_zcrx_mem {
unsigned long size;
--
2.52.0
^ permalink raw reply related
* [PATCH 3/5] io_uring/zcrx: add shared-memory notification statistics
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260422112522.3316660-1-cleger@meta.com>
Add support for an optional stats struct embedded in the refill queue
region, allowing userspace to monitor copy-fallback and no-buffers events
in real-time.
Userspace queries the stats struct size and alignment via
IO_URING_QUERY_ZCRX (notif_stats_size / notif_stats_alignment), then
provides a stats_offset in zcrx_notification_desc pointing to a location
within the refill queue region.
The kernel updates the stats counters in-place using atomic ops on every
copy-fallback and no-buffers event.
Signed-off-by: Clément Léger <cleger@meta.com>
---
include/uapi/linux/io_uring/query.h | 12 +++++++
include/uapi/linux/io_uring/zcrx.h | 15 +++++++--
io_uring/query.c | 14 ++++++++
io_uring/zcrx.c | 50 +++++++++++++++++++++++++++--
io_uring/zcrx.h | 1 +
5 files changed, 88 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/io_uring/query.h b/include/uapi/linux/io_uring/query.h
index 95500759cc13..738c35c7d05c 100644
--- a/include/uapi/linux/io_uring/query.h
+++ b/include/uapi/linux/io_uring/query.h
@@ -23,6 +23,7 @@ enum {
IO_URING_QUERY_OPCODES = 0,
IO_URING_QUERY_ZCRX = 1,
IO_URING_QUERY_SCQ = 2,
+ IO_URING_QUERY_ZCRX_NOTIF = 3,
__IO_URING_QUERY_MAX,
};
@@ -62,6 +63,17 @@ struct io_uring_query_zcrx {
__u64 __resv2;
};
+struct io_uring_query_zcrx_notif {
+ /* Bitmask of supported ZCRX_NOTIF_* flags*/
+ __u32 notif_flags;
+ /* Size of io_uring_zcrx_notif_stats */
+ __u32 notif_stats_size;
+ /* Required alignment for the stats struct within the region (ie stats_offset) */
+ __u32 notif_stats_off_alignment;
+ __u32 resv1;
+ __u64 __resv2[10];
+};
+
struct io_uring_query_scq {
/* The SQ/CQ rings header size */
__u64 hdr_size;
diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h
index e0c0079626c8..ae9bbca3004c 100644
--- a/include/uapi/linux/io_uring/zcrx.h
+++ b/include/uapi/linux/io_uring/zcrx.h
@@ -73,11 +73,22 @@ enum zcrx_notification_type {
ZCRX_NOTIF_COPY = 1 << 1
};
+enum zcrx_notification_desc_flags {
+ /* If set, stats_offset holds a valid offset to a notif_stats struct */
+ ZCRX_NOTIF_DESC_FLAG_STATS = 1 << 0,
+};
+
+struct io_uring_zcrx_notif_stats {
+ __u64 copy_count; /* cumulative copy-fallback CQEs */
+ __u64 copy_bytes; /* cumulative bytes copied */
+};
+
struct zcrx_notification_desc {
__u64 user_data;
__u32 type_mask;
- __u32 __resv1;
- __u64 __resv2[10];
+ __u32 flags; /* see enum zcrx_notification_desc_flags */
+ __u64 stats_offset; /* offset from the beginning of refill ring region for stats */
+ __u64 __resv2[9];
};
/*
diff --git a/io_uring/query.c b/io_uring/query.c
index c1704d088374..3591106e139d 100644
--- a/io_uring/query.c
+++ b/io_uring/query.c
@@ -9,6 +9,7 @@
union io_query_data {
struct io_uring_query_opcode opcodes;
struct io_uring_query_zcrx zcrx;
+ struct io_uring_query_zcrx_notif zcrx_notif;
struct io_uring_query_scq scq;
};
@@ -44,6 +45,16 @@ static ssize_t io_query_zcrx(union io_query_data *data)
return sizeof(*e);
}
+static ssize_t io_query_zcrx_notif(union io_query_data *data)
+{
+ struct io_uring_query_zcrx_notif *e = &data->zcrx_notif;
+
+ e->notif_flags = ZCRX_NOTIF_TYPE_MASK;
+ e->notif_stats_size = sizeof(struct io_uring_zcrx_notif_stats);
+ e->notif_stats_off_alignment = __alignof__(struct io_uring_zcrx_notif_stats);
+ return sizeof(*e);
+}
+
static ssize_t io_query_scq(union io_query_data *data)
{
struct io_uring_query_scq *e = &data->scq;
@@ -83,6 +94,9 @@ static int io_handle_query_entry(union io_query_data *data, void __user *uhdr,
case IO_URING_QUERY_ZCRX:
ret = io_query_zcrx(data);
break;
+ case IO_URING_QUERY_ZCRX_NOTIF:
+ ret = io_query_zcrx_notif(data);
+ break;
case IO_URING_QUERY_SCQ:
ret = io_query_scq(data);
break;
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 732e585aa13a..c61f94fb14c3 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -414,6 +414,7 @@ static void io_free_rbuf_ring(struct io_zcrx_ifq *ifq)
io_free_region(ifq->user, &ifq->rq_region);
ifq->rq.ring = NULL;
ifq->rq.rqes = NULL;
+ ifq->notif_stats = NULL;
}
static void io_zcrx_free_area(struct io_zcrx_ifq *ifq,
@@ -841,6 +842,33 @@ static int zcrx_register_netdev(struct io_zcrx_ifq *ifq,
return ret;
}
+static int zcrx_validate_notif_stats(struct io_zcrx_ifq *ifq,
+ const struct io_uring_zcrx_ifq_reg *reg,
+ const struct zcrx_notification_desc *notif)
+{
+ size_t stats_off = notif->stats_offset;
+ size_t used, end;
+
+ used = reg->offsets.rqes +
+ sizeof(struct io_uring_zcrx_rqe) * reg->rq_entries;
+
+ if (!IS_ALIGNED(stats_off, __alignof__(struct io_uring_zcrx_notif_stats)))
+ return -EINVAL;
+ if (stats_off < used)
+ return -ERANGE;
+ if (check_add_overflow(stats_off,
+ sizeof(struct io_uring_zcrx_notif_stats),
+ &end))
+ return -ERANGE;
+ if (end > io_region_size(&ifq->rq_region))
+ return -ERANGE;
+
+ ifq->notif_stats = io_region_get_ptr(&ifq->rq_region) + stats_off;
+ memset(ifq->notif_stats, 0, sizeof(*ifq->notif_stats));
+
+ return 0;
+}
+
int io_register_zcrx(struct io_ring_ctx *ctx,
struct io_uring_zcrx_ifq_reg __user *arg)
{
@@ -894,7 +922,9 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
return -EFAULT;
if (notif.type_mask & ~ZCRX_NOTIF_TYPE_MASK)
return -EINVAL;
- if (notif.__resv1 || !mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
+ if (notif.flags & ~ZCRX_NOTIF_DESC_FLAG_STATS)
+ return -EINVAL;
+ if (!mem_is_zero(¬if.__resv2, sizeof(notif.__resv2)))
return -EINVAL;
ifq = io_zcrx_ifq_alloc(ctx);
@@ -925,6 +955,12 @@ int io_register_zcrx(struct io_ring_ctx *ctx,
if (ret)
goto err;
+ if (notif.flags & ZCRX_NOTIF_DESC_FLAG_STATS) {
+ ret = zcrx_validate_notif_stats(ifq, ®, ¬if);
+ if (ret)
+ goto err;
+ }
+
ifq->kern_readable = !(area.flags & IORING_ZCRX_AREA_DMABUF);
if (!(reg.flags & ZCRX_REG_NODEV)) {
@@ -1133,6 +1169,11 @@ static void zcrx_notif_tw(struct io_tw_req tw_req, io_tw_token_t tw)
kfree_rcu(req, rcu_head);
}
+static void zcrx_stat_add(__u64 *p, s64 v)
+{
+ WRITE_ONCE(*p, READ_ONCE(*p) + v);
+}
+
static void zcrx_send_notif(struct io_zcrx_ifq *ifq, u32 type_mask)
{
gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO;
@@ -1513,8 +1554,13 @@ static int io_zcrx_copy_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
int ret;
ret = io_zcrx_copy_chunk(req, ifq, page, off + skb_frag_off(frag), len);
- if (ret > 0)
+ if (ret > 0) {
+ if (ifq->notif_stats) {
+ zcrx_stat_add(&ifq->notif_stats->copy_count, 1);
+ zcrx_stat_add(&ifq->notif_stats->copy_bytes, ret);
+ }
zcrx_send_notif(ifq, ZCRX_NOTIF_COPY);
+ }
return ret;
}
diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h
index 1bd63adaa711..0dcf486ff530 100644
--- a/io_uring/zcrx.h
+++ b/io_uring/zcrx.h
@@ -82,6 +82,7 @@ struct io_zcrx_ifq {
u32 allowed_notif_mask;
u32 fired_notifs;
u64 notif_data;
+ struct io_uring_zcrx_notif_stats *notif_stats;
};
#if defined(CONFIG_IO_URING_ZCRX)
--
2.52.0
^ permalink raw reply related
* [PATCH 4/5] Documentation: networking: document zcrx notifications and statistics
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260422112522.3316660-1-cleger@meta.com>
Document the zcrx notification system and shared-memory statistics
that were introduced to let userspace monitor zero-copy receive health.
The notification section covers the two notification types
(ZCRX_NOTIF_NO_BUFFERS, ZCRX_NOTIF_COPY), registration via
zcrx_notification_desc, and the fire-once / re-arm mechanism via
ZCRX_CTRL_ARM_NOTIFICATION. The statistics section covers the optional
shared-memory io_uring_zcrx_notif_stats structure placed in the refill
ring region, including how to query its layout via
IO_URING_QUERY_ZCRX_NOTIF.
Signed-off-by: Clément Léger <cleger@meta.com>
---
Documentation/networking/iou-zcrx.rst | 106 ++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
diff --git a/Documentation/networking/iou-zcrx.rst b/Documentation/networking/iou-zcrx.rst
index 7f3f4b2e6cf2..b17205fe55aa 100644
--- a/Documentation/networking/iou-zcrx.rst
+++ b/Documentation/networking/iou-zcrx.rst
@@ -196,6 +196,112 @@ Return buffers back to the kernel to be used again::
rqe->len = cqe->res;
IO_URING_WRITE_ONCE(*refill_ring.ktail, ++refill_ring.rq_tail);
+Notifications
+-------------
+
+When zero-copy receive encounters conditions that affect performance or
+functionality, the kernel can notify userspace via dedicated CQE notifications.
+The application must register a notification descriptor during
+``IORING_REGISTER_ZCRX_IFQ`` to receive them.
+
+Supported features can be detected by checking for ``ZCRX_FEATURE_NOTIFICATION``
+in the features bitmask returned by ``IO_URING_QUERY_ZCRX``.
+
+**Notification types**
+
+``ZCRX_NOTIF_NO_BUFFERS``
+ Fired when the page pool fails to allocate because the zcrx buffer area is
+ exhausted.
+
+``ZCRX_NOTIF_COPY``
+ Fired when a received fragment could not be delivered zero-copy and was
+ instead copied into a buffer.
+
+**Registering notifications**
+
+Allocate and fill a ``struct zcrx_notification_desc``::
+
+ struct zcrx_notification_desc notif = {
+ .user_data = MY_NOTIF_USER_DATA,
+ .type_mask = ZCRX_NOTIF_NO_BUFFERS | ZCRX_NOTIF_COPY,
+ };
+
+ reg.notif_desc = (__u64)(unsigned long)¬if;
+
+``user_data`` is the value that will appear in the notification CQE's
+``user_data`` field. ``type_mask`` selects which notification types the
+application wants to receive.
+
+When a registered event occurs, the kernel posts a CQE with the specified
+``user_data`` and ``cqe->res`` set to a bitmask of the triggered notification
+types.
+
+**Rate limiting**
+
+Each notification type fires once until the application explicitly re-arms it.
+To re-arm, issue ``IORING_REGISTER_ZCRX_CTRL`` with
+``ZCRX_CTRL_ARM_NOTIFICATION``::
+
+ struct zcrx_ctrl ctrl = {
+ .zcrx_id = zcrx_id,
+ .op = ZCRX_CTRL_ARM_NOTIFICATION,
+ .zc_arm_notif = {
+ .type_mask = ZCRX_NOTIF_NO_BUFFERS | ZCRX_NOTIF_COPY,
+ },
+ };
+
+ io_uring_register(ring_fd, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);
+
+Only notification types that have previously fired can be re-armed.
+
+Notification statistics
+-----------------------
+
+In addition to CQE-based notifications, the kernel can maintain a shared-memory
+statistics structure that is updated on every relevant event. All stats are
+updated regardless of which notification flags were registered.
+
+The statistics structure layout and alignment requirements can be queried via
+``IO_URING_QUERY_ZCRX_NOTIF``. The application must query the structure size
+and alignment requirements so that it allocates enough memory for the region
+to fit both the refill ring and the stats structure.
+
+To enable statistics, place the stats structure after the refill ring entries
+within the same mapped region, and set the ``ZCRX_NOTIF_DESC_FLAG_STATS`` flag
+in the notification descriptor::
+
+ /* Compute offset for the stats struct (after refill ring entries) */
+ size_t stats_offset = ring_size;
+ ring_size += ALIGN_UP(sizeof(struct io_uring_zcrx_notif_stats), PAGE_SIZE);
+
+ /* Map the region with the extra space */
+ ring_ptr = mmap(NULL, ring_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+
+ struct zcrx_notification_desc notif = {
+ .user_data = MY_NOTIF_USER_DATA,
+ .type_mask = ZCRX_NOTIF_COPY,
+ .flags = ZCRX_NOTIF_DESC_FLAG_STATS,
+ .stats_offset = stats_offset,
+ };
+
+The ``stats_offset`` must satisfy the alignment reported by
+``notif_stats_off_alignment`` and must point to a location within the mapped
+region that does not overlap with the refill ring header or entries.
+
+Application can read stat counters them at any time::
+
+ volatile struct io_uring_zcrx_notif_stats *stats =
+ (void *)((char *)ring_ptr + stats_offset);
+
+ printf("copy fallbacks: %llu (%llu bytes)\n",
+ IO_URING_READ_ONCE(stats->copy_count),
+ IO_URING_READ_ONCE(stats->copy_bytes));
+
+``copy_count`` is incremented each time a fragment is copied instead of being
+delivered via zero-copy. ``copy_bytes`` accumulates the total number of bytes
+copied.
+
Area chunking
-------------
--
2.52.0
^ permalink raw reply related
* [PATCH 5/5] selftests: iou-zcrx: add notification and stats test for zcrx
From: Clément Léger @ 2026-04-22 11:25 UTC (permalink / raw)
To: io-uring, Pavel Begunkov, Jens Axboe
Cc: Clément Léger, linux-doc, linux-kernel, linux-kselftest,
netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Vishwanath Seshagiri
In-Reply-To: <20260422112522.3316660-1-cleger@meta.com>
Add a selftest to verify that ZCRX notification are properly delivered
to userspace and that the shared-memory notification stats (copy_count,
copy_bytes) are correctly incremented when zero-copy RX falls back to
copying or when it runs out of buffers.
The test registers a notification descriptor during
IORING_REGISTER_ZCRX_IFQ with a stats region placed after the refill
queue entries. A new -n flag verifies that the copy fallback is
triggered and -b/-a flags allows to check for out of buffer
notification.
To reliably trigger copy fallback, the Python test uses a new
single_no_flow() setup variant that configures tcp-data-split and RSS
but without ethtool flow rule. Without flow steering, traffic arrives
on non-zcrx queues as regular pages, forcing the kernel copy-fallback
path in io_zcrx_copy_frag().
Out-of-buffer notification is verified by using a smaller receive area
and by avoiding recycling the buffers so that the kernel runs out of
buffer quickly.
Signed-off-by: Clément Léger <cleger@meta.com>
---
.../selftests/drivers/net/hw/iou-zcrx.c | 112 ++++++++++++++++--
.../selftests/drivers/net/hw/iou-zcrx.py | 49 +++++++-
2 files changed, 149 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.c b/tools/testing/selftests/drivers/net/hw/iou-zcrx.c
index 240d13dbc54e..3c95e6460c24 100644
--- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.c
+++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.c
@@ -52,7 +52,27 @@ struct t_io_uring_zcrx_ifq_reg {
struct io_uring_zcrx_offsets offsets;
__u32 zcrx_id;
__u32 rx_buf_len;
- __u64 __resv[3];
+ __u64 notif_desc;
+ __u64 __resv[2];
+};
+
+#define ZCRX_NOTIF_NO_BUFFERS (1 << 0)
+#define ZCRX_NOTIF_COPY (1 << 1)
+#define ZCRX_NOTIF_DESC_FLAG_STATS (1 << 0)
+
+#define NOTIF_USER_DATA 3
+
+struct t_zcrx_notification_desc {
+ __u64 user_data;
+ __u32 type_mask;
+ __u32 flags;
+ __u64 stats_offset;
+ __u64 __resv2[9];
+};
+
+struct t_io_uring_zcrx_notif_stats {
+ __u64 copy_count;
+ __u64 copy_bytes;
};
static long page_size;
@@ -84,7 +104,10 @@ static int cfg_oneshot_recvs;
static int cfg_send_size = SEND_SIZE;
static struct sockaddr_in6 cfg_addr;
static unsigned int cfg_rx_buf_len;
+static size_t cfg_area_size;
static bool cfg_dry_run;
+static bool cfg_copy_fallback;
+static bool cfg_no_buffers;
static char *payload;
static void *area_ptr;
@@ -95,6 +118,8 @@ static unsigned long area_token;
static int connfd;
static bool stop;
static size_t received;
+static unsigned int notif_received_mask;
+static size_t notif_stats_offset;
static unsigned long gettimeofday_ms(void)
{
@@ -142,6 +167,7 @@ static void setup_zcrx(struct io_uring *ring)
{
unsigned int ifindex;
unsigned int rq_entries = 4096;
+ size_t area_size = cfg_area_size ? cfg_area_size : AREA_SIZE;
int ret;
ifindex = if_nametoindex(cfg_ifname);
@@ -150,7 +176,7 @@ static void setup_zcrx(struct io_uring *ring)
if (cfg_rx_buf_len && cfg_rx_buf_len != page_size) {
area_ptr = mmap(NULL,
- AREA_SIZE,
+ area_size,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE |
MAP_HUGETLB | MAP_HUGE_2MB,
@@ -162,7 +188,7 @@ static void setup_zcrx(struct io_uring *ring)
}
} else {
area_ptr = mmap(NULL,
- AREA_SIZE,
+ area_size,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
0,
@@ -172,6 +198,12 @@ static void setup_zcrx(struct io_uring *ring)
}
ring_size = get_refill_ring_size(rq_entries);
+
+ if (cfg_copy_fallback) {
+ notif_stats_offset = ring_size;
+ ring_size += ALIGN_UP(sizeof(struct t_io_uring_zcrx_notif_stats), page_size);
+ }
+
ring_ptr = mmap(NULL,
ring_size,
PROT_READ | PROT_WRITE,
@@ -187,10 +219,11 @@ static void setup_zcrx(struct io_uring *ring)
struct io_uring_zcrx_area_reg area_reg = {
.addr = (__u64)(unsigned long)area_ptr,
- .len = AREA_SIZE,
+ .len = area_size,
.flags = 0,
};
+ struct t_zcrx_notification_desc notif_desc;
struct t_io_uring_zcrx_ifq_reg reg = {
.if_idx = ifindex,
.if_rxq = cfg_queue_id,
@@ -200,11 +233,32 @@ static void setup_zcrx(struct io_uring *ring)
.rx_buf_len = cfg_rx_buf_len,
};
+ if (cfg_copy_fallback || cfg_no_buffers) {
+ __u32 type_mask = 0;
+
+ if (cfg_copy_fallback)
+ type_mask = ZCRX_NOTIF_COPY;
+ if (cfg_no_buffers)
+ type_mask = ZCRX_NOTIF_NO_BUFFERS;
+
+ memset(¬if_desc, 0, sizeof(notif_desc));
+ notif_desc.user_data = NOTIF_USER_DATA;
+ notif_desc.type_mask = type_mask;
+ if (cfg_copy_fallback) {
+ notif_desc.flags = ZCRX_NOTIF_DESC_FLAG_STATS;
+ notif_desc.stats_offset = notif_stats_offset;
+ }
+ reg.notif_desc = (__u64)(unsigned long)¬if_desc;
+ }
+
ret = io_uring_register_ifq(ring, (void *)®);
if (cfg_rx_buf_len && (ret == -EINVAL || ret == -EOPNOTSUPP ||
ret == -ERANGE)) {
printf("Large chunks are not supported %i\n", ret);
exit(SKIP_CODE);
+ } else if ((cfg_copy_fallback || cfg_no_buffers) && ret == -EINVAL) {
+ printf("Notifications not supported %i\n", ret);
+ exit(SKIP_CODE);
} else if (ret) {
error(1, 0, "io_uring_register_ifq(): %d", ret);
}
@@ -304,10 +358,13 @@ static void process_recvzc(struct io_uring *ring, struct io_uring_cqe *cqe)
}
received += n;
- rqe = &rq_ring.rqes[(rq_ring.rq_tail & rq_mask)];
- rqe->off = (rcqe->off & ~IORING_ZCRX_AREA_MASK) | area_token;
- rqe->len = cqe->res;
- io_uring_smp_store_release(rq_ring.ktail, ++rq_ring.rq_tail);
+ /* Skip ring refill so that we ran out of buffers quickly */
+ if (!cfg_no_buffers) {
+ rqe = &rq_ring.rqes[(rq_ring.rq_tail & rq_mask)];
+ rqe->off = (rcqe->off & ~IORING_ZCRX_AREA_MASK) | area_token;
+ rqe->len = cqe->res;
+ io_uring_smp_store_release(rq_ring.ktail, ++rq_ring.rq_tail);
+ }
}
static void server_loop(struct io_uring *ring)
@@ -324,8 +381,15 @@ static void server_loop(struct io_uring *ring)
process_accept(ring, cqe);
else if (cqe->user_data == 2)
process_recvzc(ring, cqe);
- else
+ else if ((cfg_copy_fallback || cfg_no_buffers) &&
+ cqe->user_data == NOTIF_USER_DATA) {
+ notif_received_mask |= cqe->res;
+ if (cfg_no_buffers &&
+ (cqe->res & ZCRX_NOTIF_NO_BUFFERS))
+ stop = true;
+ } else {
error(1, 0, "unknown cqe");
+ }
count++;
}
io_uring_cq_advance(ring, count);
@@ -374,6 +438,23 @@ static void run_server(void)
if (!stop)
error(1, 0, "test failed\n");
+
+ if (cfg_copy_fallback) {
+ struct t_io_uring_zcrx_notif_stats *stats =
+ (void *)((char *)ring_ptr + notif_stats_offset);
+
+ if (!(notif_received_mask & ZCRX_NOTIF_COPY))
+ error(1, 0, "expected copy fallback notification");
+ if (!IO_URING_READ_ONCE(stats->copy_count))
+ error(1, 0, "expected copy_count > 0");
+ if (!IO_URING_READ_ONCE(stats->copy_bytes))
+ error(1, 0, "expected copy_bytes > 0");
+ }
+
+ if (cfg_no_buffers) {
+ if (!(notif_received_mask & ZCRX_NOTIF_NO_BUFFERS))
+ error(1, 0, "expected no-buffers notification");
+ }
}
static void run_client(void)
@@ -425,7 +506,7 @@ static void parse_opts(int argc, char **argv)
usage(argv[0]);
cfg_payload_len = max_payload_len;
- while ((c = getopt(argc, argv, "sch:p:l:i:q:o:z:x:d")) != -1) {
+ while ((c = getopt(argc, argv, "sch:p:l:i:q:o:z:x:a:dnb")) != -1) {
switch (c) {
case 's':
if (cfg_client)
@@ -466,8 +547,19 @@ static void parse_opts(int argc, char **argv)
case 'd':
cfg_dry_run = true;
break;
+ case 'n':
+ cfg_copy_fallback = true;
+ break;
+ case 'b':
+ cfg_no_buffers = true;
+ break;
+ case 'a':
+ cfg_area_size = strtoul(optarg, NULL, 0) * page_size;
+ break;
}
}
+ if (cfg_copy_fallback && cfg_no_buffers)
+ error(1, 0, "Pass one of -n or -b");
if (cfg_server && addr)
error(1, 0, "Receiver cannot have -h specified");
diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py
index e81724cb5542..f7f1cbff5959 100755
--- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py
+++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py
@@ -41,7 +41,9 @@ def set_flow_rule_rss(cfg, rss_ctx_id):
return int(values)
-def single(cfg):
+def single_no_flow(cfg):
+ """Like single() but without a flow rule."""
+
channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
channels = channels['combined-count']
if channels < 2:
@@ -65,6 +67,9 @@ def single(cfg):
ethtool(f"-X {cfg.ifname} equal {cfg.target}")
defer(ethtool, f"-X {cfg.ifname} default")
+def single(cfg):
+ single_no_flow(cfg)
+
flow_rule_id = set_flow_rule(cfg)
defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
@@ -130,6 +135,26 @@ def test_zcrx_oneshot(cfg, setup) -> None:
cmd(tx_cmd, host=cfg.remote)
+@ksft_variants([
+ KsftNamedVariant("single", single_no_flow),
+])
+def test_zcrx_notif(cfg, setup) -> None:
+ """Test zcrx copy fallback notification.
+
+ Omits the flow rule so traffic arrives on non-zcrx queues as regular
+ pages, forcing the kernel copy-fallback path. Asserts that the
+ ZCRX_NOTIF_COPY notification CQE is delivered."""
+
+ cfg.require_ipver('6')
+
+ setup(cfg)
+ rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target} -n"
+ tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840"
+ with bkg(rx_cmd, exit_wait=True):
+ wait_port_listen(cfg.port, proto="tcp")
+ cmd(tx_cmd, host=cfg.remote)
+
+
def test_zcrx_large_chunks(cfg) -> None:
"""Test zcrx with large buffer chunks."""
@@ -157,6 +182,25 @@ def test_zcrx_large_chunks(cfg) -> None:
cmd(tx_cmd, host=cfg.remote)
+@ksft_variants([
+ KsftNamedVariant("single", single),
+])
+def test_zcrx_notif_no_buffers(cfg, setup) -> None:
+ """Test zcrx out-of-buffer notification.
+
+ Skips buffer refill so the pool is quickly exhausted, triggering
+ a ZCRX_NOTIF_NO_BUFFERS notification CQE."""
+
+ cfg.require_ipver('6')
+
+ setup(cfg)
+ rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target} -b -a 64"
+ tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840"
+ with bkg(rx_cmd, exit_wait=True):
+ wait_port_listen(cfg.port, proto="tcp")
+ cmd(tx_cmd, host=cfg.remote, fail=False)
+
+
def main() -> None:
with NetDrvEpEnv(__file__) as cfg:
cfg.bin_local = path.abspath(path.dirname(__file__) + "/../../../drivers/net/hw/iou-zcrx")
@@ -166,7 +210,8 @@ def main() -> None:
cfg.netnl = NetdevFamily()
cfg.port = rand_port()
ksft_run(globs=globals(), cases=[test_zcrx, test_zcrx_oneshot,
- test_zcrx_large_chunks], args=(cfg, ))
+ test_zcrx_large_chunks, test_zcrx_notif,
+ test_zcrx_notif_no_buffers], args=(cfg, ))
ksft_exit()
--
2.52.0
^ permalink raw reply related
* Re: [PATCH] ipv6: udp: fix memory leak in udpv6_sendmsg error path
From: Sabrina Dubroca @ 2026-04-22 11:55 UTC (permalink / raw)
To: Mingyu Wang
Cc: willemdebruijn.kernel, davem, dsahern, edumazet, kuba, pabeni,
horms, netdev, linux-kernel
In-Reply-To: <20260422105802.486216-1-25181214217@stu.xidian.edu.cn>
2026-04-22, 18:58:02 +0800, Mingyu Wang wrote:
> During fuzzing with failslab enabled, a memory leak was observed in the
> IPv6 UDP send path.
>
> When sending via the lockless fast path (!corkreq), udpv6_sendmsg()
> calls ip6_make_skb() and assumes that the routing entry (dst_entry)
> reference has been stolen by the callee. However, if ip6_make_skb()
> fails early (e.g., due to an ENOMEM from memory allocation failure),
> it returns an error pointer without consuming the dst reference.
Not in all cases? If ip6_setup_cork() fails, we call
ip6_cork_release() which will release the dst. The MSG_PROBE path also
releases the dst. __ip6_flush_pending_frames() also looks like it does
that.
> Since udpv6_sendmsg() unconditionally jumps to the 'out_no_dst' label,
> the unconsumed dst_entry is never released, resulting in a memory leak.
>
> Fix this by explicitly calling dst_release(dst) when ip6_make_skb()
> returns an error.
>
> Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
And this is missing a Fixes tag.
> net/ipv6/udp.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 15e032194ecc..b83ecfd729af 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -1706,8 +1706,11 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
> dst_rt6_info(dst),
> msg->msg_flags, &cork);
> err = PTR_ERR(skb);
> - if (!IS_ERR_OR_NULL(skb))
> + if (!IS_ERR_OR_NULL(skb)) {
> err = udp_v6_send_skb(skb, fl6, &cork.base);
> + } else {
> + dst_release(dst);
> + }
> /* ip6_make_skb steals dst reference */
This comment becomes really confusing after your patch.
> goto out_no_dst;
> }
> --
> 2.34.1
>
>
--
Sabrina
^ permalink raw reply
* Re: [PATCH net-next] Documentation: net/smc: correct old value of smcr_max_recv_wr
From: Alexandra Winter @ 2026-04-22 12:08 UTC (permalink / raw)
To: Mahanta Jambigi, andrew+netdev, davem, edumazet, kuba, pabeni,
alibuda, dust.li, sidraya, wenjia
Cc: pasic, horms, tonylu, guwen, netdev, linux-s390
In-Reply-To: <20260422085159.459678-1-mjambigi@linux.ibm.com>
net-next is closed!
On 22.04.26 10:51, Mahanta Jambigi wrote:
> The smc-sysctl.rst documentation incorrectly stated that the previous
> hardcoded maximum number of WR buffers on the receive path (smcr_max_recv_wr)
> was 16. The correct historical value used before the introduction of the sysctl
> control was 48. Update the documentation to reflect the accurate default value.
s/default/historical/
or remove the last sentence
>
> Fixes: aef3cdb47bbb net/smc: make wr buffer count configurable
> Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
> Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
> Signed-off-by: Mahanta Jambigi <mjambigi@linux.ibm.com>
> ---
> Documentation/networking/smc-sysctl.rst | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/networking/smc-sysctl.rst b/Documentation/networking/smc-sysctl.rst
> index 904a910f198e..279d15e61899 100644
> --- a/Documentation/networking/smc-sysctl.rst
> +++ b/Documentation/networking/smc-sysctl.rst
> @@ -100,14 +100,14 @@ smcr_max_recv_wr - INTEGER
> depending on the workload it can be a bottleneck in a sense that threads
> have to wait for work request buffers to become available. Before the
> introduction of this control the maximal number of work request buffers
> - available on the receive path used to be hard coded to 16. With this control
> + available on the receive path used to be hard coded to 48. With this control
> it becomes configurable. The acceptable range is between 2 and 2048.
>
> Please be aware that all the buffers need to be allocated as a physically
> continuous array in which each element is a single buffer and has the size
> of SMC_WR_BUF_SIZE (48) bytes. If the allocation fails, we keep retrying
> with half of the buffer count until it is ether successful or (unlikely)
typo in old documentation: s/ether/either/g
> - we dip below the old hard coded value which is 16 where we give up much
> + we dip below the old hard coded value which is 48 where we give up much
> like before having this control.
>
> Default: 48
^ permalink raw reply
* [PATCH] mptcp: do not drop partial packets
From: Shardul Bankar @ 2026-04-22 12:09 UTC (permalink / raw)
To: matttbe, martineau
Cc: geliang, pabeni, davem, edumazet, kuba, horms, netdev, mptcp,
linux-kernel, janak, kalpan.jani, shardulsb08, Shardul Bankar
When a packet arrives with map_seq < ack_seq < end_seq, the beginning
of the packet has already been acknowledged but the end contains new
data. Currently the entire packet is dropped as "old data," forcing
the sender to retransmit.
Instead, skip the already-acked bytes by adjusting the skb offset and
enqueue only the new portion. Update bytes_received and ack_seq to
reflect the new data consumed.
A previous attempt at this fix (commit 1d2ce718811a ("mptcp: do not
drop partial packets"), reverted in commit bf39160c4218 ("Revert
"mptcp: do not drop partial packets"")) also added a zero-window
check and changed rcv_wnd_sent initialization, which caused test
regressions. This version addresses only the partial packet handling
without modifying receive window accounting.
Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.")
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
---
net/mptcp/protocol.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 614c3f583ca0..6858e6e283e3 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -397,12 +397,27 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
return false;
}
- /* old data, keep it simple and drop the whole pkt, sender
- * will retransmit as needed, if needed.
+ /* Completely old data? */
+ if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
+ mptcp_drop(sk, skb);
+ return false;
+ }
+
+ /* Partial packet: map_seq < ack_seq < end_seq.
+ * Skip the already-acked bytes and enqueue the new data.
*/
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
- mptcp_drop(sk, skb);
- return false;
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+ MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
+ msk->bytes_received += copy_len;
+ WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
+ tail = skb_peek_tail(&sk->sk_receive_queue);
+ if (tail && mptcp_try_coalesce(sk, tail, skb))
+ return true;
+
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+ return true;
}
static void mptcp_stop_rtx_timer(struct sock *sk)
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net 00/18] Remove a number of ISA and PCMCIA Ethernet drivers
From: Andrew Lunn @ 2026-04-22 12:11 UTC (permalink / raw)
To: Byron Stanoszek
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <9a0bc592-fb74-f646-1752-4359c0ac31a2@polinggroup.com>
On Tue, Apr 21, 2026 at 11:03:28PM -0400, Byron Stanoszek wrote:
> On Wed, 22 Apr 2026, Andrew Lunn wrote:
> >
> > Could you live with v6.18, which has an expected EOL of December 2028?
> > If you are only updating once per year, security is not an issue, you
> > just want stability.
>
> I could for the time being, but this hasn't worked for me in the past. Usually
> what happens is the PC breaks down, and the customer swaps in a new
> backplane+SBC and moves all their PCI cards over. I then find I need to update
> the kernel just to get the Intel DRM to work properly on the new CPU. Some of
> these systems were installed back in the Linux 2.6 era, so I've gone through
> several "Intel DRM not working" steps ever since CPUs started getting
> integrated graphics. 2028 will come fast.
Hi Byron
I will drop this driver from the patchset.
Andrew
^ permalink raw reply
* Re: [PATCH net 11/18] drivers: net: cirrus: cs89x0: Remove this driver
From: Andrew Lunn @ 2026-04-22 12:13 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <CAMuHMdWz=ucmKxHXmzKj=oTn6yMVxPnkNxtG6X2C3ts_ZCg4Cw@mail.gmail.com>
> > -config CS89x0_PLATFORM
> > - tristate "CS89x0 platform driver support"
> > - depends on ARM || (COMPILE_TEST && !PPC)
> > - select CS89x0
> > - help
> > - Say Y to compile the cs89x0 platform driver. This makes this driver
> > - suitable for use on certain evaluation boards such as the iMX21ADS.
> > -
> > - To compile this driver as a module, choose M here. The module
> > - will be called cs89x0.
>
> This is the more modern DT-based part...
No dependency on OF?
> However, no users of these compatible values ever appeared upstream.
Thanks for the information. That helps with the removal.
Andrew
^ permalink raw reply
* Re: [PATCH net 14/18] drivers: net: xircom: xirc2ps: Remove this driver
From: Andrew Lunn @ 2026-04-22 12:15 UTC (permalink / raw)
To: Michael Fritscher
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, linux-kernel, netdev,
linux-doc
In-Reply-To: <73e3a34c-f1dc-403b-b007-18ff85d66ea1@fritscher.net>
On Wed, Apr 22, 2026 at 08:21:23AM +0200, Michael Fritscher wrote:
> Good day,
>
> actually, I do use Xircom PCMCIA network cards (yes, the 16 bit ones) on
> Lenovo X60/X61 laptops as a second LAN card for server maintenances with
> current 64 bit distros (e.g. Debian Trixie, which I plan to update to
> Trixie+1 when available). Why? Because I have them and they are working ;-)
Hi Michael
I will drop this from the patchset for the moment.
Would you be willing to take up the Maintainer role for it?
Andrew
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next] ice: init desired_dcbx_cfg in default DCB config
From: Arland, ArpanaX @ 2026-04-22 12:16 UTC (permalink / raw)
To: Loktionov, Aleksandr, intel-wired-lan@lists.osuosl.org,
Nguyen, Anthony L, Loktionov, Aleksandr
Cc: netdev@vger.kernel.org, Czapnik, Lukasz
In-Reply-To: <20260320050541.422592-1-aleksandr.loktionov@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Aleksandr Loktionov
> Sent: Friday, March 20, 2026 10:36 AM
> To: intel-wired-lan@lists.osuosl.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Loktionov, Aleksandr <aleksandr.loktionov@intel.com>
> Cc: netdev@vger.kernel.org; Czapnik, Lukasz <lukasz.czapnik@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-next] ice: init desired_dcbx_cfg in default DCB config
>
> From: Lukasz Czapnik <lukasz.czapnik@intel.com>
>
> When DCBX is disabled in firmware the driver falls back to software LLDP mode and applies a default DCB configuration via ice_dcb_sw_dflt_cfg().
> This function properly initializes local_dcbx_cfg with valid parameters including etscfg.maxtcs from hardware capabilities. However, desired_dcbx_cfg was never initialized in this path.
>
> All DCB netlink functions (ice_dcbnl_setpfc, ice_dcbnl_setets, etc.) use desired_dcbx_cfg as the base configuration for user-requested changes.
> When desired_dcbx_cfg remains uninitialized with etscfg.maxtcs=0, the firmware rejects the configuration for 4+ port NICs, causing DCB configuration commands to fail.
>
> It is not a problem for 1 or 2 port NICs where we support 8 TCs - in that case FW accepts maxtc=0, treating it as 8.
>
> Fix it by copying local_dcbx_cfg (which was freshly initialized) into desired_dcbx_cfg after the default config is applied.
>
> Fixes: b94b013eb626 ("ice: Implement DCBNL support")
> Signed-off-by: Lukasz Czapnik <lukasz.czapnik@intel.com>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 3 +++
> 1 file changed, 3 insertions(+)
>
Tested-by: Arpana Arland <arpanax.arland@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* Re: [PATCH net 11/18] drivers: net: cirrus: cs89x0: Remove this driver
From: Geert Uytterhoeven @ 2026-04-22 12:17 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
linux-kernel, netdev, linux-doc
In-Reply-To: <c6696785-6f1f-4747-996a-d86a60a23d0b@lunn.ch>
Hi Andrew,
On Wed, 22 Apr 2026 at 14:13, Andrew Lunn <andrew@lunn.ch> wrote:
> > > -config CS89x0_PLATFORM
> > > - tristate "CS89x0 platform driver support"
> > > - depends on ARM || (COMPILE_TEST && !PPC)
> > > - select CS89x0
> > > - help
> > > - Say Y to compile the cs89x0 platform driver. This makes this driver
> > > - suitable for use on certain evaluation boards such as the iMX21ADS.
> > > -
> > > - To compile this driver as a module, choose M here. The module
> > > - will be called cs89x0.
> >
> > This is the more modern DT-based part...
>
> No dependency on OF?
I guess no one bothered to add it, as it presumably builds fine without.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next] ice: reduce loglevel to debug for 'Can't delete DSCP' message
From: Arland, ArpanaX @ 2026-04-22 12:17 UTC (permalink / raw)
To: Loktionov, Aleksandr, intel-wired-lan@lists.osuosl.org,
Nguyen, Anthony L, Loktionov, Aleksandr
Cc: netdev@vger.kernel.org
In-Reply-To: <20260320050548.422684-1-aleksandr.loktionov@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Aleksandr Loktionov
> Sent: Friday, March 20, 2026 10:36 AM
> To: intel-wired-lan@lists.osuosl.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Loktionov, Aleksandr <aleksandr.loktionov@intel.com>
> Cc: netdev@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH iwl-next] ice: reduce loglevel to debug for 'Can't delete DSCP' message
>
> From: Grzegorz Nitka <grzegorz.nitka@intel.com>
>
> Reduce netdev message "Can't delete DSCP netlink app ..." from error to debug level when in FW (firmware) DCB mode.
> This is not a real error and a kind of expected behavior. The device is fully operational and error message might be wrongly interpreted by the user.
>
> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_dcb_nl.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Tested-by: Arpana Arland <arpanax.arland@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox