From e8e032cd24dda7cceaa27bc2eb627f82843f0466 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 28 Nov 2025 10:59:15 +0800 Subject: [PATCH 001/214] net: fec: ERR007885 Workaround for XDP TX path The ERR007885 will lead to a TDAR race condition for mutliQ when the driver sets TDAR and the UDMA clears TDAR simultaneously or in a small window (2-4 cycles). And it will cause the udma_tx and udma_tx_arbiter state machines to hang. Therefore, the commit 53bb20d1faba ("net: fec: add variable reg_desc_active to speed things up") and the commit a179aad12bad ("net: fec: ERR007885 Workaround for conventional TX") have added the workaround to fix the potential issue for the conventional TX path. Similarly, the XDP TX path should also have the potential hang issue, so add the workaround for XDP TX path. Fixes: 6d6b39f180b8 ("net: fec: add initial XDP support") Signed-off-by: Wei Fang Link: https://patch.msgid.link/20251128025915.2486943-1-wei.fang@nxp.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/fec_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index c685a5c0cc51a..a753265961af5 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3933,7 +3933,12 @@ static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, txq->bd.cur = bdp; /* Trigger transmission start */ - writel(0, txq->bd.reg_desc_active); + if (!(fep->quirks & FEC_QUIRK_ERR007885) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active)) + writel(0, txq->bd.reg_desc_active); return 0; } From 613d12dd794e078be8ff3cf6b62a6b9acf7f4619 Mon Sep 17 00:00:00 2001 From: Wang Liang Date: Sat, 29 Nov 2025 12:13:15 +0800 Subject: [PATCH 002/214] netrom: Fix memory leak in nr_sendmsg() syzbot reported a memory leak [1]. When function sock_alloc_send_skb() return NULL in nr_output(), the original skb is not freed, which was allocated in nr_sendmsg(). Fix this by freeing it before return. [1] BUG: memory leak unreferenced object 0xffff888129f35500 (size 240): comm "syz.0.17", pid 6119, jiffies 4294944652 hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 10 52 28 81 88 ff ff ..........R(.... backtrace (crc 1456a3e4): kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline] slab_post_alloc_hook mm/slub.c:4983 [inline] slab_alloc_node mm/slub.c:5288 [inline] kmem_cache_alloc_node_noprof+0x36f/0x5e0 mm/slub.c:5340 __alloc_skb+0x203/0x240 net/core/skbuff.c:660 alloc_skb include/linux/skbuff.h:1383 [inline] alloc_skb_with_frags+0x69/0x3f0 net/core/skbuff.c:6671 sock_alloc_send_pskb+0x379/0x3e0 net/core/sock.c:2965 sock_alloc_send_skb include/net/sock.h:1859 [inline] nr_sendmsg+0x287/0x450 net/netrom/af_netrom.c:1105 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] sock_write_iter+0x293/0x2a0 net/socket.c:1195 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x45d/0x710 fs/read_write.c:686 ksys_write+0x143/0x170 fs/read_write.c:738 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xa4/0xfa0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Reported-by: syzbot+d7abc36bbbb6d7d40b58@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=d7abc36bbbb6d7d40b58 Tested-by: syzbot+d7abc36bbbb6d7d40b58@syzkaller.appspotmail.com Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Wang Liang Link: https://patch.msgid.link/20251129041315.1550766-1-wangliang74@huawei.com Signed-off-by: Paolo Abeni --- net/netrom/nr_out.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index 5e531394a724b..2b3cbceb0b52d 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -43,8 +43,10 @@ void nr_output(struct sock *sk, struct sk_buff *skb) frontlen = skb_headroom(skb); while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) + if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) { + kfree_skb(skb); return; + } skb_reserve(skbn, frontlen); From 188e0fa5a679570ea35474575e724d8211423d17 Mon Sep 17 00:00:00 2001 From: Shaurya Rane Date: Sat, 29 Nov 2025 15:07:18 +0530 Subject: [PATCH 003/214] net/hsr: fix NULL pointer dereference in prp_get_untagged_frame() prp_get_untagged_frame() calls __pskb_copy() to create frame->skb_std but doesn't check if the allocation failed. If __pskb_copy() returns NULL, skb_clone() is called with a NULL pointer, causing a crash: Oops: general protection fault, probably for non-canonical address 0xdffffc000000000f: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000078-0x000000000000007f] CPU: 0 UID: 0 PID: 5625 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 RIP: 0010:skb_clone+0xd7/0x3a0 net/core/skbuff.c:2041 Code: 03 42 80 3c 20 00 74 08 4c 89 f7 e8 23 29 05 f9 49 83 3e 00 0f 85 a0 01 00 00 e8 94 dd 9d f8 48 8d 6b 7e 49 89 ee 49 c1 ee 03 <43> 0f b6 04 26 84 c0 0f 85 d1 01 00 00 44 0f b6 7d 00 41 83 e7 0c RSP: 0018:ffffc9000d00f200 EFLAGS: 00010207 RAX: ffffffff892235a1 RBX: 0000000000000000 RCX: ffff88803372a480 RDX: 0000000000000000 RSI: 0000000000000820 RDI: 0000000000000000 RBP: 000000000000007e R08: ffffffff8f7d0f77 R09: 1ffffffff1efa1ee R10: dffffc0000000000 R11: fffffbfff1efa1ef R12: dffffc0000000000 R13: 0000000000000820 R14: 000000000000000f R15: ffff88805144cc00 FS: 0000555557f6d500(0000) GS:ffff88808d72f000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000555581d35808 CR3: 000000005040e000 CR4: 0000000000352ef0 Call Trace: hsr_forward_do net/hsr/hsr_forward.c:-1 [inline] hsr_forward_skb+0x1013/0x2860 net/hsr/hsr_forward.c:741 hsr_handle_frame+0x6ce/0xa70 net/hsr/hsr_slave.c:84 __netif_receive_skb_core+0x10b9/0x4380 net/core/dev.c:5966 __netif_receive_skb_one_core net/core/dev.c:6077 [inline] __netif_receive_skb+0x72/0x380 net/core/dev.c:6192 netif_receive_skb_internal net/core/dev.c:6278 [inline] netif_receive_skb+0x1cb/0x790 net/core/dev.c:6337 tun_rx_batched+0x1b9/0x730 drivers/net/tun.c:1485 tun_get_user+0x2b65/0x3e90 drivers/net/tun.c:1953 tun_chr_write_iter+0x113/0x200 drivers/net/tun.c:1999 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x5c9/0xb30 fs/read_write.c:686 ksys_write+0x145/0x250 fs/read_write.c:738 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0xfa0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f0449f8e1ff Code: 89 54 24 18 48 89 74 24 10 89 7c 24 08 e8 f9 92 02 00 48 8b 54 24 18 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 31 44 89 c7 48 89 44 24 08 e8 4c 93 02 00 48 RSP: 002b:00007ffd7ad94c90 EFLAGS: 00000293 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007f044a1e5fa0 RCX: 00007f0449f8e1ff RDX: 000000000000003e RSI: 0000200000000500 RDI: 00000000000000c8 RBP: 00007ffd7ad94d20 R08: 0000000000000000 R09: 0000000000000000 R10: 000000000000003e R11: 0000000000000293 R12: 0000000000000001 R13: 00007f044a1e5fa0 R14: 00007f044a1e5fa0 R15: 0000000000000003 Add a NULL check immediately after __pskb_copy() to handle allocation failures gracefully. Reported-by: syzbot+2fa344348a579b779e05@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=2fa344348a579b779e05 Fixes: f266a683a480 ("net/hsr: Better frame dispatch") Cc: stable@vger.kernel.org Signed-off-by: Shaurya Rane Reviewed-by: Felix Maurer Tested-by: Felix Maurer Link: https://patch.msgid.link/20251129093718.25320-1-ssrane_b23@ee.vjti.ac.in Signed-off-by: Paolo Abeni --- net/hsr/hsr_forward.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 339f0d2202129..aefc9b6936ba0 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -205,6 +205,8 @@ struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, __pskb_copy(frame->skb_prp, skb_headroom(frame->skb_prp), GFP_ATOMIC); + if (!frame->skb_std) + return NULL; } else { /* Unexpected */ WARN_ONCE(1, "%s:%d: Unexpected frame received (port_src %s)\n", From ce052b9402e461a9aded599f5b47e76bc727f7de Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Fri, 28 Nov 2025 10:19:19 -0500 Subject: [PATCH 004/214] net/sched: ets: Always remove class from active list before deleting in ets_qdisc_change zdi-disclosures@trendmicro.com says: The vulnerability is a race condition between `ets_qdisc_dequeue` and `ets_qdisc_change`. It leads to UAF on `struct Qdisc` object. Attacker requires the capability to create new user and network namespace in order to trigger the bug. See my additional commentary at the end of the analysis. Analysis: static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { ... // (1) this lock is preventing .change handler (`ets_qdisc_change`) //to race with .dequeue handler (`ets_qdisc_dequeue`) sch_tree_lock(sch); for (i = nbands; i < oldbands; i++) { if (i >= q->nstrict && q->classes[i].qdisc->q.qlen) list_del_init(&q->classes[i].alist); qdisc_purge_queue(q->classes[i].qdisc); } WRITE_ONCE(q->nbands, nbands); for (i = nstrict; i < q->nstrict; i++) { if (q->classes[i].qdisc->q.qlen) { // (2) the class is added to the q->active list_add_tail(&q->classes[i].alist, &q->active); q->classes[i].deficit = quanta[i]; } } WRITE_ONCE(q->nstrict, nstrict); memcpy(q->prio2band, priomap, sizeof(priomap)); for (i = 0; i < q->nbands; i++) WRITE_ONCE(q->classes[i].quantum, quanta[i]); for (i = oldbands; i < q->nbands; i++) { q->classes[i].qdisc = queues[i]; if (q->classes[i].qdisc != &noop_qdisc) qdisc_hash_add(q->classes[i].qdisc, true); } // (3) the qdisc is unlocked, now dequeue can be called in parallel // to the rest of .change handler sch_tree_unlock(sch); ets_offload_change(sch); for (i = q->nbands; i < oldbands; i++) { // (4) we're reducing the refcount for our class's qdisc and // freeing it qdisc_put(q->classes[i].qdisc); // (5) If we call .dequeue between (4) and (5), we will have // a strong UAF and we can control RIP q->classes[i].qdisc = NULL; WRITE_ONCE(q->classes[i].quantum, 0); q->classes[i].deficit = 0; gnet_stats_basic_sync_init(&q->classes[i].bstats); memset(&q->classes[i].qstats, 0, sizeof(q->classes[i].qstats)); } return 0; } Comment: This happens because some of the classes have their qdiscs assigned to NULL, but remain in the active list. This commit fixes this issue by always removing the class from the active list before deleting and freeing its associated qdisc Reproducer Steps (trimmed version of what was sent by zdi-disclosures@trendmicro.com) ``` DEV="${DEV:-lo}" ROOT_HANDLE="${ROOT_HANDLE:-1:}" BAND2_HANDLE="${BAND2_HANDLE:-20:}" # child under 1:2 PING_BYTES="${PING_BYTES:-48}" PING_COUNT="${PING_COUNT:-200000}" PING_DST="${PING_DST:-127.0.0.1}" SLOW_TBF_RATE="${SLOW_TBF_RATE:-8bit}" SLOW_TBF_BURST="${SLOW_TBF_BURST:-100b}" SLOW_TBF_LAT="${SLOW_TBF_LAT:-1s}" cleanup() { tc qdisc del dev "$DEV" root 2>/dev/null } trap cleanup EXIT ip link set "$DEV" up tc qdisc del dev "$DEV" root 2>/dev/null || true tc qdisc add dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2 tc qdisc add dev "$DEV" parent 1:2 handle "$BAND2_HANDLE" \ tbf rate "$SLOW_TBF_RATE" burst "$SLOW_TBF_BURST" latency "$SLOW_TBF_LAT" tc filter add dev "$DEV" parent 1: protocol all prio 1 u32 match u32 0 0 flowid 1:2 tc -s qdisc ls dev $DEV ping -I "$DEV" -f -c "$PING_COUNT" -s "$PING_BYTES" -W 0.001 "$PING_DST" \ >/dev/null 2>&1 & tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 0 tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2 tc -s qdisc ls dev $DEV tc qdisc del dev "$DEV" parent 1:2 || true tc -s qdisc ls dev $DEV tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 1 strict 1 ``` KASAN report ``` ================================================================== BUG: KASAN: slab-use-after-free in ets_qdisc_dequeue+0x1071/0x11b0 kernel/net/sched/sch_ets.c:481 Read of size 8 at addr ffff8880502fc018 by task ping/12308 > CPU: 0 UID: 0 PID: 12308 Comm: ping Not tainted 6.18.0-rc4-dirty #1 PREEMPT(full) Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: __dump_stack kernel/lib/dump_stack.c:94 dump_stack_lvl+0x100/0x190 kernel/lib/dump_stack.c:120 print_address_description kernel/mm/kasan/report.c:378 print_report+0x156/0x4c9 kernel/mm/kasan/report.c:482 kasan_report+0xdf/0x110 kernel/mm/kasan/report.c:595 ets_qdisc_dequeue+0x1071/0x11b0 kernel/net/sched/sch_ets.c:481 dequeue_skb kernel/net/sched/sch_generic.c:294 qdisc_restart kernel/net/sched/sch_generic.c:399 __qdisc_run+0x1c9/0x1b00 kernel/net/sched/sch_generic.c:417 __dev_xmit_skb kernel/net/core/dev.c:4221 __dev_queue_xmit+0x2848/0x4410 kernel/net/core/dev.c:4729 dev_queue_xmit kernel/./include/linux/netdevice.h:3365 [...] Allocated by task 17115: kasan_save_stack+0x30/0x50 kernel/mm/kasan/common.c:56 kasan_save_track+0x14/0x30 kernel/mm/kasan/common.c:77 poison_kmalloc_redzone kernel/mm/kasan/common.c:400 __kasan_kmalloc+0xaa/0xb0 kernel/mm/kasan/common.c:417 kasan_kmalloc kernel/./include/linux/kasan.h:262 __do_kmalloc_node kernel/mm/slub.c:5642 __kmalloc_node_noprof+0x34e/0x990 kernel/mm/slub.c:5648 kmalloc_node_noprof kernel/./include/linux/slab.h:987 qdisc_alloc+0xb8/0xc30 kernel/net/sched/sch_generic.c:950 qdisc_create_dflt+0x93/0x490 kernel/net/sched/sch_generic.c:1012 ets_class_graft+0x4fd/0x800 kernel/net/sched/sch_ets.c:261 qdisc_graft+0x3e4/0x1780 kernel/net/sched/sch_api.c:1196 [...] Freed by task 9905: kasan_save_stack+0x30/0x50 kernel/mm/kasan/common.c:56 kasan_save_track+0x14/0x30 kernel/mm/kasan/common.c:77 __kasan_save_free_info+0x3b/0x70 kernel/mm/kasan/generic.c:587 kasan_save_free_info kernel/mm/kasan/kasan.h:406 poison_slab_object kernel/mm/kasan/common.c:252 __kasan_slab_free+0x5f/0x80 kernel/mm/kasan/common.c:284 kasan_slab_free kernel/./include/linux/kasan.h:234 slab_free_hook kernel/mm/slub.c:2539 slab_free kernel/mm/slub.c:6630 kfree+0x144/0x700 kernel/mm/slub.c:6837 rcu_do_batch kernel/kernel/rcu/tree.c:2605 rcu_core+0x7c0/0x1500 kernel/kernel/rcu/tree.c:2861 handle_softirqs+0x1ea/0x8a0 kernel/kernel/softirq.c:622 __do_softirq kernel/kernel/softirq.c:656 [...] Commentary: 1. Maher Azzouzi working with Trend Micro Zero Day Initiative was reported as the person who found the issue. I requested to get a proper email to add to the reported-by tag but got no response. For this reason i will credit the person i exchanged emails with i.e zdi-disclosures@trendmicro.com 2. Neither i nor Victor who did a much more thorough testing was able to reproduce a UAF with the PoC or other approaches we tried. We were both able to reproduce a null ptr deref. After exchange with zdi-disclosures@trendmicro.com they sent a small change to be made to the code to add an extra delay which was able to simulate the UAF. i.e, this: qdisc_put(q->classes[i].qdisc); mdelay(90); q->classes[i].qdisc = NULL; I was informed by Thomas Gleixner(tglx@linutronix.de) that adding delays was acceptable approach for demonstrating the bug, quote: "Adding such delays is common exploit validation practice" The equivalent delay could happen "by virt scheduling the vCPU out, SMIs, NMIs, PREEMPT_RT enabled kernel" 3. I asked the OP to test and report back but got no response and after a few days gave up and proceeded to submit this fix. Fixes: de6d25924c2a ("net/sched: sch_ets: don't peek at classes beyond 'nbands'") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Davide Caratti Link: https://patch.msgid.link/20251128151919.576920-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/sch_ets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index 82635dd2cfa59..ae46643e596d3 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -652,7 +652,7 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, sch_tree_lock(sch); for (i = nbands; i < oldbands; i++) { - if (i >= q->nstrict && q->classes[i].qdisc->q.qlen) + if (cl_is_active(&q->classes[i])) list_del_init(&q->classes[i].alist); qdisc_purge_queue(q->classes[i].qdisc); } From cd7671ef4cf2edf73cd2a3dca3a2f522a4525bf5 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Mon, 1 Dec 2025 17:13:27 +0200 Subject: [PATCH 005/214] net/mlx5: make enable_mpesw idempotent The enable_mpesw() function returns -EINVAL if ldev->mode is not MLX5_LAG_MODE_NONE. This means attempting to enable MPESW mode when it's already enabled will fail. In contrast, disable_mpesw() properly checks if the mode is MLX5_LAG_MODE_MPESW before proceeding, making it naturally idempotent and safe to call multiple times. Fix enable_mpesw() to return success if mpesw is already enabled. Fixes: a32327a3a02c ("net/mlx5: Lag, Control MultiPort E-Switch single FDB mode") Signed-off-by: Moshe Shemesh Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Reviewed-by: Simon Horman Link: https://patch.msgid.link/1764602008-1334866-2-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index aad52d3a90e68..2d86af8f0d9b8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -67,12 +67,19 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev) static int enable_mpesw(struct mlx5_lag *ldev) { - int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); struct mlx5_core_dev *dev0; int err; + int idx; int i; - if (idx < 0 || ldev->mode != MLX5_LAG_MODE_NONE) + if (ldev->mode == MLX5_LAG_MODE_MPESW) + return 0; + + if (ldev->mode != MLX5_LAG_MODE_NONE) + return -EINVAL; + + idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); + if (idx < 0) return -EINVAL; dev0 = ldev->pf[idx].dev; From 35e93736f69963337912594eb3951ab320b77521 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Mon, 1 Dec 2025 17:13:28 +0200 Subject: [PATCH 006/214] net/mlx5e: Avoid unregistering PSP twice PSP is unregistered twice in: _mlx5e_remove -> mlx5e_psp_unregister mlx5e_nic_cleanup -> mlx5e_psp_unregister This leads to a refcount underflow in some conditions: ------------[ cut here ]------------ refcount_t: underflow; use-after-free. WARNING: CPU: 2 PID: 1694 at lib/refcount.c:28 refcount_warn_saturate+0xd8/0xe0 [...] mlx5e_psp_unregister+0x26/0x50 [mlx5_core] mlx5e_nic_cleanup+0x26/0x90 [mlx5_core] mlx5e_remove+0xe6/0x1f0 [mlx5_core] auxiliary_bus_remove+0x18/0x30 device_release_driver_internal+0x194/0x1f0 bus_remove_device+0xc6/0x130 device_del+0x159/0x3c0 mlx5_rescan_drivers_locked+0xbc/0x2a0 [mlx5_core] [...] Do not directly remove psp from the _mlx5e_remove path, the PSP cleanup happens as part of profile cleanup. Fixes: 89ee2d92f66c ("net/mlx5e: Support PSP offload functionality") Signed-off-by: Cosmin Ratiu Reviewed-by: Dragos Tatulea Signed-off-by: Tariq Toukan Reviewed-by: Simon Horman Link: https://patch.msgid.link/1764602008-1334866-3-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6168f08144148..07fc4d2c8fadd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -6825,7 +6825,6 @@ static void _mlx5e_remove(struct auxiliary_device *adev) * is already unregistered before changing to NIC profile. */ if (priv->netdev->reg_state == NETREG_REGISTERED) { - mlx5e_psp_unregister(priv); unregister_netdev(priv->netdev); _mlx5e_suspend(adev, false); } else { From 4f0638b12451112de4138689fa679315c8d388dc Mon Sep 17 00:00:00 2001 From: Ivan Galkin Date: Tue, 2 Dec 2025 10:07:42 +0100 Subject: [PATCH 007/214] net: phy: RTL8211FVD: Restore disabling of PHY-mode EEE When support for RTL8211F(D)(I)-VD-CG was introduced in commit bb726b753f75 ("net: phy: realtek: add support for RTL8211F(D)(I)-VD-CG") the implementation assumed that this PHY model doesn't have the control register PHYCR2 (Page 0xa43 Address 0x19). This assumption was based on the differences in CLKOUT configurations between RTL8211FVD and the remaining RTL8211F PHYs. In the latter commit 2c67301584f2 ("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present") this assumption was expanded to the PHY-mode EEE. I performed tests on RTL8211FI-VD-CG and confirmed that disabling PHY-mode EEE works correctly and is uniform with other PHYs supported by the driver. To validate the correctness, I contacted Realtek support. Realtek confirmed that PHY-mode EEE on RTL8211F(D)(I)-VD-CG is configured via Page 0xa43 Address 0x19 bit 5. Moreover, Realtek informed me that the most recent datasheet for RTL8211F(D)(I)-VD-CG v1.1 is incomplete and the naming of control registers is partly inconsistent. The errata I received from Realtek corrects the naming as follows: | Register | Datasheet v1.1 | Errata | |-------------------------|----------------|--------| | Page 0xa44 Address 0x11 | PHYCR2 | PHYCR3 | | Page 0xa43 Address 0x19 | N/A | PHYCR2 | This information confirms that the supposedly missing control register, PHYCR2, exists in the RTL8211F(D)(I)-VD-CG under the same address and the same name. It controls widely the same configs as other PHYs from the RTL8211F series (e.g. PHY-mode EEE). Clock out configuration is an exception. Given all this information, restore disabling of the PHY-mode EEE. Fixes: 2c67301584f2 ("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present") Signed-off-by: Ivan Galkin Reviewed-by: Vladimir Oltean Link: https://patch.msgid.link/20251202-phy_eee-v1-1-fe0bf6ab3df0@axis.com Signed-off-by: Paolo Abeni --- drivers/net/phy/realtek/realtek_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 67ecf3d4af2b1..6ff0385201a57 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -691,10 +691,6 @@ static int rtl8211f_config_aldps(struct phy_device *phydev) static int rtl8211f_config_phy_eee(struct phy_device *phydev) { - /* RTL8211FVD has no PHYCR2 register */ - if (phydev->drv->phy_id == RTL_8211FVD_PHYID) - return 0; - /* Disable PHY-mode EEE so LPI is passed to the MAC */ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); From 5b48f49ee94888f3cd4360286ee9921eff2b2e46 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 2 Dec 2025 09:57:21 +0000 Subject: [PATCH 008/214] net: dsa: mxl-gsw1xx: fix SerDes RX polarity According to MaxLinear engineer Benny Weng the RX lane of the SerDes port of the GSW1xx switches is inverted in hardware, and the SGMII_PHY_RX0_CFG2_INVERT bit is set by default in order to compensate for that. Hence also set the SGMII_PHY_RX0_CFG2_INVERT bit by default in gsw1xx_pcs_reset(). Fixes: 22335939ec90 ("net: dsa: add driver for MaxLinear GSW1xx switch family") Reported-by: Rasmus Villemoes Signed-off-by: Daniel Golle Reviewed-by: Vladimir Oltean Link: https://patch.msgid.link/ca10e9f780c0152ecf9ae8cbac5bf975802e8f99.1764668951.git.daniel@makrotopia.org Signed-off-by: Paolo Abeni --- drivers/net/dsa/lantiq/mxl-gsw1xx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c index 0816c61a47f12..cf33a16fd183b 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.c +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c @@ -255,10 +255,16 @@ static int gsw1xx_pcs_reset(struct gsw1xx_priv *priv) FIELD_PREP(GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT, GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT_DEF); - /* TODO: Take care of inverted RX pair once generic property is + /* RX lane seems to be inverted internally, so bit + * GSW1XX_SGMII_PHY_RX0_CFG2_INVERT needs to be set for normal + * (ie. non-inverted) operation. + * + * TODO: Take care of inverted RX pair once generic property is * available */ + val |= GSW1XX_SGMII_PHY_RX0_CFG2_INVERT; + ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_RX0_CFG2, val); if (ret < 0) return ret; From 0c57ff008a11f24f7f05fa760222692a00465fec Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Tue, 2 Dec 2025 13:39:03 +0300 Subject: [PATCH 009/214] ipvlan: Ignore PACKET_LOOPBACK in handle_mode_l2() Packets with pkt_type == PACKET_LOOPBACK are captured by handle_frame() function, but they don't have L2 header. We should not process them in handle_mode_l2(). This doesn't affect old L2 functionality, since handling was anyway incorrect. Handle them the same way as in br_handle_frame(): just pass the skb. To observe invalid behaviour, just start "ping -b" on bcast address of port-interface. Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.") Signed-off-by: Dmitry Skorodumov Link: https://patch.msgid.link/20251202103906.4087675-1-skorodumov.dmitry@huawei.com Signed-off-by: Paolo Abeni --- drivers/net/ipvlan/ipvlan_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index dea411e132dba..2efa3ba148aa7 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -737,6 +737,9 @@ static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb, struct ethhdr *eth = eth_hdr(skb); rx_handler_result_t ret = RX_HANDLER_PASS; + if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) + return RX_HANDLER_PASS; + if (is_multicast_ether_addr(eth->h_dest)) { if (ipvlan_external_frame(skb, port)) { struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); From 6a107cfe9c99a079e578a4c5eb70038101a3599f Mon Sep 17 00:00:00 2001 From: Gerd Bayer Date: Tue, 2 Dec 2025 12:12:57 +0100 Subject: [PATCH 010/214] net/mlx5: Fix double unregister of HCA_PORTS component Clear hca_devcom_comp in device's private data after unregistering it in LAG teardown. Otherwise a slightly lagging second pass through mlx5_unload_one() might try to unregister it again and trip over use-after-free. On s390 almost all PCI level recovery events trigger two passes through mxl5_unload_one() - one through the poll_health() method and one through mlx5_pci_err_detected() as callback from generic PCI error recovery. While testing PCI error recovery paths with more kernel debug features enabled, this issue reproducibly led to kernel panics with the following call chain: Unable to handle kernel pointer dereference in virtual kernel address space Failing address: 6b6b6b6b6b6b6000 TEID: 6b6b6b6b6b6b6803 ESOP-2 FSI Fault in home space mode while using kernel ASCE. AS:00000000705c4007 R3:0000000000000024 Oops: 0038 ilc:3 [#1]SMP CPU: 14 UID: 0 PID: 156 Comm: kmcheck Kdump: loaded Not tainted 6.18.0-20251130.rc7.git0.16131a59cab1.300.fc43.s390x+debug #1 PREEMPT Krnl PSW : 0404e00180000000 0000020fc86aa1dc (__lock_acquire+0x5c/0x15f0) R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3 Krnl GPRS: 0000000000000000 0000020f00000001 6b6b6b6b6b6b6c33 0000000000000000 0000000000000000 0000000000000000 0000000000000001 0000000000000000 0000000000000000 0000020fca28b820 0000000000000000 0000010a1ced8100 0000010a1ced8100 0000020fc9775068 0000018fce14f8b8 0000018fce14f7f8 Krnl Code: 0000020fc86aa1cc: e3b003400004 lg %r11,832 0000020fc86aa1d2: a7840211 brc 8,0000020fc86aa5f4 *0000020fc86aa1d6: c09000df0b25 larl %r9,0000020fca28b820 >0000020fc86aa1dc: d50790002000 clc 0(8,%r9),0(%r2) 0000020fc86aa1e2: a7840209 brc 8,0000020fc86aa5f4 0000020fc86aa1e6: c0e001100401 larl %r14,0000020fca8aa9e8 0000020fc86aa1ec: c01000e25a00 larl %r1,0000020fca2f55ec 0000020fc86aa1f2: a7eb00e8 aghi %r14,232 Call Trace: __lock_acquire+0x5c/0x15f0 lock_acquire.part.0+0xf8/0x270 lock_acquire+0xb0/0x1b0 down_write+0x5a/0x250 mlx5_detach_device+0x42/0x110 [mlx5_core] mlx5_unload_one_devl_locked+0x50/0xc0 [mlx5_core] mlx5_unload_one+0x42/0x60 [mlx5_core] mlx5_pci_err_detected+0x94/0x150 [mlx5_core] zpci_event_attempt_error_recovery+0xcc/0x388 Fixes: 5a977b5833b7 ("net/mlx5: Lag, move devcom registration to LAG layer") Signed-off-by: Gerd Bayer Reviewed-by: Moshe Shemesh Acked-by: Tariq Toukan Link: https://patch.msgid.link/20251202-fix_lag-v1-1-59e8177ffce0@linux.ibm.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 1ac933cd8f02b..a459a30f36cae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -1413,6 +1413,7 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev) static void mlx5_lag_unregister_hca_devcom_comp(struct mlx5_core_dev *dev) { mlx5_devcom_unregister_component(dev->priv.hca_devcom_comp); + dev->priv.hca_devcom_comp = NULL; } static int mlx5_lag_register_hca_devcom_comp(struct mlx5_core_dev *dev) From c4cdf7376271bce5714c06d79ec67759b18910eb Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 2 Dec 2025 18:27:44 +0100 Subject: [PATCH 011/214] net: phy: marvell-88q2xxx: Fix clamped value in mv88q2xxx_hwmon_write The local variable 'val' was never clamped to -75000 or 180000 because the return value of clamp_val() was not used. Fix this by assigning the clamped value back to 'val', and use clamp() instead of clamp_val(). Cc: stable@vger.kernel.org Fixes: a557a92e6881 ("net: phy: marvell-88q2xxx: add support for temperature sensor") Signed-off-by: Thorsten Blum Reviewed-by: Dimitri Fedrau Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20251202172743.453055-3-thorsten.blum@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/phy/marvell-88q2xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c index f3d83b04c9535..201dee1a16985 100644 --- a/drivers/net/phy/marvell-88q2xxx.c +++ b/drivers/net/phy/marvell-88q2xxx.c @@ -698,7 +698,7 @@ static int mv88q2xxx_hwmon_write(struct device *dev, switch (attr) { case hwmon_temp_max: - clamp_val(val, -75000, 180000); + val = clamp(val, -75000, 180000); val = (val / 1000) + 75; val = FIELD_PREP(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, val); From b6b638bda240395dff49a87403b2e32493e56d2a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 2 Dec 2025 18:44:11 +0100 Subject: [PATCH 012/214] mlxsw: spectrum_router: Fix possible neighbour reference count leak mlxsw_sp_router_schedule_work() takes a reference on a neighbour, expecting a work item to release it later on. However, we might fail to schedule the work item, in which case the neighbour reference count will be leaked. Fix by taking the reference just before scheduling the work item. Note that mlxsw_sp_router_schedule_work() can receive a NULL neighbour pointer, but neigh_clone() handles that correctly. Spotted during code review, did not actually observe the reference count leak. Fixes: 151b89f6025a ("mlxsw: spectrum_router: Reuse work neighbor initialization in work scheduler") Reviewed-by: Petr Machata Signed-off-by: Ido Schimmel Signed-off-by: Petr Machata Reviewed-by: Simon Horman Link: https://patch.msgid.link/ec2934ae4aca187a8d8c9329a08ce93cca411378.1764695650.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index a2033837182e8..f4e9ecaeb104f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2858,6 +2858,11 @@ static int mlxsw_sp_router_schedule_work(struct net *net, if (!net_work) return NOTIFY_BAD; + /* Take a reference to ensure the neighbour won't be destructed until + * we drop the reference in the work item. + */ + neigh_clone(n); + INIT_WORK(&net_work->work, cb); net_work->mlxsw_sp = router->mlxsw_sp; net_work->n = n; @@ -2881,11 +2886,6 @@ static int mlxsw_sp_router_schedule_neigh_work(struct mlxsw_sp_router *router, struct net *net; net = neigh_parms_net(n->parms); - - /* Take a reference to ensure the neighbour won't be destructed until we - * drop the reference in delayed work. - */ - neigh_clone(n); return mlxsw_sp_router_schedule_work(net, router, n, mlxsw_sp_router_neigh_event_work); } From 8b0e69763ef948fb872a7767df4be665d18f5fd4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 2 Dec 2025 18:44:12 +0100 Subject: [PATCH 013/214] mlxsw: spectrum_router: Fix neighbour use-after-free We sometimes observe use-after-free when dereferencing a neighbour [1]. The problem seems to be that the driver stores a pointer to the neighbour, but without holding a reference on it. A reference is only taken when the neighbour is used by a nexthop. Fix by simplifying the reference counting scheme. Always take a reference when storing a neighbour pointer in a neighbour entry. Avoid taking a referencing when the neighbour is used by a nexthop as the neighbour entry associated with the nexthop already holds a reference. Tested by running the test that uncovered the problem over 300 times. Without this patch the problem was reproduced after a handful of iterations. [1] BUG: KASAN: slab-use-after-free in mlxsw_sp_neigh_entry_update+0x2d4/0x310 Read of size 8 at addr ffff88817f8e3420 by task ip/3929 CPU: 3 UID: 0 PID: 3929 Comm: ip Not tainted 6.18.0-rc4-virtme-g36b21a067510 #3 PREEMPT(full) Hardware name: Nvidia SN5600/VMOD0013, BIOS 5.13 05/31/2023 Call Trace: dump_stack_lvl+0x6f/0xa0 print_address_description.constprop.0+0x6e/0x300 print_report+0xfc/0x1fb kasan_report+0xe4/0x110 mlxsw_sp_neigh_entry_update+0x2d4/0x310 mlxsw_sp_router_rif_gone_sync+0x35f/0x510 mlxsw_sp_rif_destroy+0x1ea/0x730 mlxsw_sp_inetaddr_port_vlan_event+0xa1/0x1b0 __mlxsw_sp_inetaddr_lag_event+0xcc/0x130 __mlxsw_sp_inetaddr_event+0xf5/0x3c0 mlxsw_sp_router_netdevice_event+0x1015/0x1580 notifier_call_chain+0xcc/0x150 call_netdevice_notifiers_info+0x7e/0x100 __netdev_upper_dev_unlink+0x10b/0x210 netdev_upper_dev_unlink+0x79/0xa0 vrf_del_slave+0x18/0x50 do_set_master+0x146/0x7d0 do_setlink.isra.0+0x9a0/0x2880 rtnl_newlink+0x637/0xb20 rtnetlink_rcv_msg+0x6fe/0xb90 netlink_rcv_skb+0x123/0x380 netlink_unicast+0x4a3/0x770 netlink_sendmsg+0x75b/0xc90 __sock_sendmsg+0xbe/0x160 ____sys_sendmsg+0x5b2/0x7d0 ___sys_sendmsg+0xfd/0x180 __sys_sendmsg+0x124/0x1c0 do_syscall_64+0xbb/0xfd0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 [...] Allocated by task 109: kasan_save_stack+0x30/0x50 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x7b/0x90 __kmalloc_noprof+0x2c1/0x790 neigh_alloc+0x6af/0x8f0 ___neigh_create+0x63/0xe90 mlxsw_sp_nexthop_neigh_init+0x430/0x7e0 mlxsw_sp_nexthop_type_init+0x212/0x960 mlxsw_sp_nexthop6_group_info_init.constprop.0+0x81f/0x1280 mlxsw_sp_nexthop6_group_get+0x392/0x6a0 mlxsw_sp_fib6_entry_create+0x46a/0xfd0 mlxsw_sp_router_fib6_replace+0x1ed/0x5f0 mlxsw_sp_router_fib6_event_work+0x10a/0x2a0 process_one_work+0xd57/0x1390 worker_thread+0x4d6/0xd40 kthread+0x355/0x5b0 ret_from_fork+0x1d4/0x270 ret_from_fork_asm+0x11/0x20 Freed by task 154: kasan_save_stack+0x30/0x50 kasan_save_track+0x14/0x30 __kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kmem_cache_free_bulk.part.0+0x1eb/0x5e0 kvfree_rcu_bulk+0x1f2/0x260 kfree_rcu_work+0x130/0x1b0 process_one_work+0xd57/0x1390 worker_thread+0x4d6/0xd40 kthread+0x355/0x5b0 ret_from_fork+0x1d4/0x270 ret_from_fork_asm+0x11/0x20 Last potentially related work creation: kasan_save_stack+0x30/0x50 kasan_record_aux_stack+0x8c/0xa0 kvfree_call_rcu+0x93/0x5b0 mlxsw_sp_router_neigh_event_work+0x67d/0x860 process_one_work+0xd57/0x1390 worker_thread+0x4d6/0xd40 kthread+0x355/0x5b0 ret_from_fork+0x1d4/0x270 ret_from_fork_asm+0x11/0x20 Fixes: 6cf3c971dc84 ("mlxsw: spectrum_router: Add private neigh table") Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Simon Horman Link: https://patch.msgid.link/92d75e21d95d163a41b5cea67a15cd33f547cba6.1764695650.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f4e9ecaeb104f..2d0e89bd2fb9c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2265,6 +2265,7 @@ mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, if (!neigh_entry) return NULL; + neigh_hold(n); neigh_entry->key.n = n; neigh_entry->rif = rif; INIT_LIST_HEAD(&neigh_entry->nexthop_list); @@ -2274,6 +2275,7 @@ mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry) { + neigh_release(neigh_entry->key.n); kfree(neigh_entry); } @@ -4320,6 +4322,8 @@ mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp, if (err) goto err_neigh_entry_insert; + neigh_release(old_n); + read_lock_bh(&n->lock); nud_state = n->nud_state; dead = n->dead; @@ -4328,14 +4332,10 @@ mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp, list_for_each_entry(nh, &neigh_entry->nexthop_list, neigh_list_node) { - neigh_release(old_n); - neigh_clone(n); __mlxsw_sp_nexthop_neigh_update(nh, !entry_connected); mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp); } - neigh_release(n); - return 0; err_neigh_entry_insert: @@ -4428,6 +4428,11 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, } } + /* Release the reference taken by neigh_lookup() / neigh_create() since + * neigh_entry already holds one. + */ + neigh_release(n); + /* If that is the first nexthop connected to that neigh, add to * nexthop_neighs_list */ @@ -4454,11 +4459,9 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry; - struct neighbour *n; if (!neigh_entry) return; - n = neigh_entry->key.n; __mlxsw_sp_nexthop_neigh_update(nh, true); list_del(&nh->neigh_list_node); @@ -4472,8 +4475,6 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list)) mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); - - neigh_release(n); } static bool mlxsw_sp_ipip_netdev_ul_up(struct net_device *ol_dev) From 8ac1dacec458f55f871f7153242ed6ab60373b90 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 2 Dec 2025 18:44:13 +0100 Subject: [PATCH 014/214] mlxsw: spectrum_mr: Fix use-after-free when updating multicast route stats Cited commit added a dedicated mutex (instead of RTNL) to protect the multicast route list, so that it will not change while the driver periodically traverses it in order to update the kernel about multicast route stats that were queried from the device. One instance of list entry deletion (during route replace) was missed and it can result in a use-after-free [1]. Fix by acquiring the mutex before deleting the entry from the list and releasing it afterwards. [1] BUG: KASAN: slab-use-after-free in mlxsw_sp_mr_stats_update+0x4a5/0x540 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c:1006 [mlxsw_spectrum] Read of size 8 at addr ffff8881523c2fa8 by task kworker/2:5/22043 CPU: 2 UID: 0 PID: 22043 Comm: kworker/2:5 Not tainted 6.18.0-rc1-custom-g1a3d6d7cd014 #1 PREEMPT(full) Hardware name: Mellanox Technologies Ltd. MSN2010/SA002610, BIOS 5.6.5 08/24/2017 Workqueue: mlxsw_core mlxsw_sp_mr_stats_update [mlxsw_spectrum] Call Trace: dump_stack_lvl+0xba/0x110 print_report+0x174/0x4f5 kasan_report+0xdf/0x110 mlxsw_sp_mr_stats_update+0x4a5/0x540 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c:1006 [mlxsw_spectrum] process_one_work+0x9cc/0x18e0 worker_thread+0x5df/0xe40 kthread+0x3b8/0x730 ret_from_fork+0x3e9/0x560 ret_from_fork_asm+0x1a/0x30 Allocated by task 29933: kasan_save_stack+0x30/0x50 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 mlxsw_sp_mr_route_add+0xd8/0x4770 [mlxsw_spectrum] mlxsw_sp_router_fibmr_event_work+0x371/0xad0 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:7965 [mlxsw_spectrum] process_one_work+0x9cc/0x18e0 worker_thread+0x5df/0xe40 kthread+0x3b8/0x730 ret_from_fork+0x3e9/0x560 ret_from_fork_asm+0x1a/0x30 Freed by task 29933: kasan_save_stack+0x30/0x50 kasan_save_track+0x14/0x30 __kasan_save_free_info+0x3b/0x70 __kasan_slab_free+0x43/0x70 kfree+0x14e/0x700 mlxsw_sp_mr_route_add+0x2dea/0x4770 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c:444 [mlxsw_spectrum] mlxsw_sp_router_fibmr_event_work+0x371/0xad0 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:7965 [mlxsw_spectrum] process_one_work+0x9cc/0x18e0 worker_thread+0x5df/0xe40 kthread+0x3b8/0x730 ret_from_fork+0x3e9/0x560 ret_from_fork_asm+0x1a/0x30 Fixes: f38656d06725 ("mlxsw: spectrum_mr: Protect multicast route list with a lock") Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Petr Machata Reviewed-by: Simon Horman Link: https://patch.msgid.link/f996feecfd59fde297964bfc85040b6d83ec6089.1764695650.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c index 5afe6b155ef0d..81935f87bfcd7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c @@ -440,7 +440,9 @@ int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table, rhashtable_remove_fast(&mr_table->route_ht, &mr_orig_route->ht_node, mlxsw_sp_mr_route_ht_params); + mutex_lock(&mr_table->route_list_lock); list_del(&mr_orig_route->node); + mutex_unlock(&mr_table->route_list_lock); mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route); } From dd75c723ef566f7f009c047f47e0eee95fe348ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Tue, 2 Dec 2025 19:41:37 +0100 Subject: [PATCH 015/214] r8169: fix RTL8117 Wake-on-Lan in DASH mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wake-on-Lan does currently not work for r8169 in DASH mode, e.g. the ASUS Pro WS X570-ACE with RTL8168fp/RTL8117. Fix by not returning early in rtl_prepare_power_down when dash_enabled. While this fixes WoL, it still kills the OOB RTL8117 remote management BMC connection. Fix by not calling rtl8168_driver_stop if WoL is enabled. Fixes: 065c27c184d6 ("r8169: phy power ops") Signed-off-by: René Rebe Cc: stable@vger.kernel.org Reviewed-by: Heiner Kallweit Link: https://patch.msgid.link/20251202.194137.1647877804487085954.rene@exactco.de Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/r8169_main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 405e91eb3141f..755083852eef2 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -2655,9 +2655,6 @@ static void rtl_wol_enable_rx(struct rtl8169_private *tp) static void rtl_prepare_power_down(struct rtl8169_private *tp) { - if (tp->dash_enabled) - return; - if (tp->mac_version == RTL_GIGA_MAC_VER_32 || tp->mac_version == RTL_GIGA_MAC_VER_33) rtl_ephy_write(tp, 0x19, 0xff64); @@ -4812,7 +4809,7 @@ static void rtl8169_down(struct rtl8169_private *tp) rtl_disable_exit_l1(tp); rtl_prepare_power_down(tp); - if (tp->dash_type != RTL_DASH_NONE) + if (tp->dash_type != RTL_DASH_NONE && !tp->saved_wolopts) rtl8168_driver_stop(tp); } From a479a27f4da4d1f8a9b7540a800f80253ed1bad0 Mon Sep 17 00:00:00 2001 From: Tim Hostetler Date: Tue, 2 Dec 2025 20:02:07 +0000 Subject: [PATCH 016/214] gve: Move gve_init_clock to after AQ CONFIGURE_DEVICE_RESOURCES call commit 46e7860ef941 ("gve: Move ptp_schedule_worker to gve_init_clock") moved the first invocation of the AQ command REPORT_NIC_TIMESTAMP to gve_probe(). However, gve_init_clock() invoking REPORT_NIC_TIMESTAMP is not valid until after gve_probe() invokes the AQ command CONFIGURE_DEVICE_RESOURCES. Failure to do so results in the following error: gve 0000:00:07.0: failed to read NIC clock -11 This was missed earlier because the driver under test was loaded at runtime instead of boot-time. The boot-time driver had already initialized the device, causing the runtime driver to successfully call gve_init_clock() incorrectly. Fixes: 46e7860ef941 ("gve: Move ptp_schedule_worker to gve_init_clock") Reviewed-by: Ankit Garg Signed-off-by: Tim Hostetler Signed-off-by: Harshitha Ramamurthy Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251202200207.1434749-1-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_main.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index a5a2b18d309b8..a7a088a77f378 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -647,12 +647,9 @@ static int gve_setup_device_resources(struct gve_priv *priv) err = gve_alloc_counter_array(priv); if (err) goto abort_with_rss_config_cache; - err = gve_init_clock(priv); - if (err) - goto abort_with_counter; err = gve_alloc_notify_blocks(priv); if (err) - goto abort_with_clock; + goto abort_with_counter; err = gve_alloc_stats_report(priv); if (err) goto abort_with_ntfy_blocks; @@ -683,10 +680,16 @@ static int gve_setup_device_resources(struct gve_priv *priv) } } + err = gve_init_clock(priv); + if (err) { + dev_err(&priv->pdev->dev, "Failed to init clock"); + goto abort_with_ptype_lut; + } + err = gve_init_rss_config(priv, priv->rx_cfg.num_queues); if (err) { dev_err(&priv->pdev->dev, "Failed to init RSS config"); - goto abort_with_ptype_lut; + goto abort_with_clock; } err = gve_adminq_report_stats(priv, priv->stats_report_len, @@ -698,6 +701,8 @@ static int gve_setup_device_resources(struct gve_priv *priv) gve_set_device_resources_ok(priv); return 0; +abort_with_clock: + gve_teardown_clock(priv); abort_with_ptype_lut: kvfree(priv->ptype_lut_dqo); priv->ptype_lut_dqo = NULL; @@ -705,8 +710,6 @@ static int gve_setup_device_resources(struct gve_priv *priv) gve_free_stats_report(priv); abort_with_ntfy_blocks: gve_free_notify_blocks(priv); -abort_with_clock: - gve_teardown_clock(priv); abort_with_counter: gve_free_counter_array(priv); abort_with_rss_config_cache: From 0373d5c387f24de749cc22e694a14b3a7c7eb515 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 2 Dec 2025 16:30:24 -0800 Subject: [PATCH 017/214] bnxt_en: Fix XDP_TX path For XDP_TX action in bnxt_rx_xdp(), clearing of the event flags is not correct. __bnxt_poll_work() -> bnxt_rx_pkt() -> bnxt_rx_xdp() may be looping within NAPI and some event flags may be set in earlier iterations. In particular, if BNXT_TX_EVENT is set earlier indicating some XDP_TX packets are ready and pending, it will be cleared if it is XDP_TX action again. Normally, we will set BNXT_TX_EVENT again when we successfully call __bnxt_xmit_xdp(). But if the TX ring has no more room, the flag will not be set. This will cause the TX producer to be ahead but the driver will not hit the TX doorbell. For multi-buf XDP_TX, there is no need to clear the event flags and set BNXT_AGG_EVENT. The BNXT_AGG_EVENT flag should have been set earlier in bnxt_rx_pkt(). The visible symptom of this is that the RX ring associated with the TX XDP ring will eventually become empty and all packets will be dropped. Because this condition will cause the driver to not refill the RX ring seeing that the TX ring has forever pending XDP_TX packets. The fix is to only clear BNXT_RX_EVENT when we have successfully called __bnxt_xmit_xdp(). Fixes: 7f0a168b0441 ("bnxt_en: Add completion ring pointer in TX and RX ring structures") Reported-by: Pavel Dubovitsky Reviewed-by: Andy Gospodarek Reviewed-by: Pavan Chebbi Reviewed-by: Kalesh AP Signed-off-by: Michael Chan Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20251203003024.2246699-1-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 3e77a96e5a3e3..c94a391b1ba5b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -268,13 +268,11 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, case XDP_TX: rx_buf = &rxr->rx_buf_ring[cons]; mapping = rx_buf->mapping - bp->rx_dma_offset; - *event &= BNXT_TX_CMP_EVENT; if (unlikely(xdp_buff_has_frags(xdp))) { struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); tx_needed += sinfo->nr_frags; - *event = BNXT_AGG_EVENT; } if (tx_avail < tx_needed) { @@ -287,6 +285,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, dma_sync_single_for_device(&pdev->dev, mapping + offset, *len, bp->rx_dir); + *event &= ~BNXT_RX_EVENT; *event |= BNXT_TX_EVENT; __bnxt_xmit_xdp(bp, txr, mapping + offset, *len, NEXT_RX(rxr->rx_prod), xdp); From 2183a5c8a04f554d03174ddcfd0078b44217fa54 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Wed, 3 Dec 2025 11:01:22 +0100 Subject: [PATCH 018/214] af_unix: annotate unix_gc_lock with __cacheline_aligned_in_smp Otherwise the lock is susceptible to ever-changing false-sharing due to unrelated changes. This in particular popped up here where an unrelated change improved performance: https://lore.kernel.org/oe-lkp/202511281306.51105b46-lkp@intel.com/ Stabilize it with an explicit annotation which also has a side effect of furher improving scalability: > in our oiginal report, 284922f4c5 has a 6.1% performance improvement comparing > to parent 17d85f33a8. > we applied your patch directly upon 284922f4c5. as below, now by > "284922f4c5 + your patch" > we observe a 12.8% performance improvements (still comparing to 17d85f33a8). Note nothing was done for the other fields, so some fluctuation is still possible. Tested-by: kernel test robot Signed-off-by: Mateusz Guzik Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20251203100122.291550-1-mjguzik@gmail.com Signed-off-by: Jakub Kicinski --- net/unix/garbage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 78323d43e63ed..25f65817faab9 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -199,7 +199,7 @@ static void unix_free_vertices(struct scm_fp_list *fpl) } } -static DEFINE_SPINLOCK(unix_gc_lock); +static __cacheline_aligned_in_smp DEFINE_SPINLOCK(unix_gc_lock); void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver) { From e9e5047df953c9b1054d9a3c7b07c68ab2714263 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 3 Dec 2025 21:44:17 -0800 Subject: [PATCH 019/214] mptcp: select CRYPTO_LIB_UTILS instead of CRYPTO Since the only crypto functions used by the mptcp code are the SHA-256 library functions and crypto_memneq(), select only the options needed for those: CRYPTO_LIB_SHA256 and CRYPTO_LIB_UTILS. Previously, CRYPTO was selected instead of CRYPTO_LIB_UTILS. That does pull in CRYPTO_LIB_UTILS as well, but it's unnecessarily broad. Years ago, the CRYPTO_LIB_* options were visible only when CRYPTO. That may be another reason why CRYPTO is selected here. However, that was fixed years ago, and the libraries can now be selected directly. Signed-off-by: Eric Biggers Reviewed-by: Mat Martineau Link: https://patch.msgid.link/20251204054417.491439-1-ebiggers@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mptcp/Kconfig b/net/mptcp/Kconfig index 20328920f6ed1..be71fc9b46381 100644 --- a/net/mptcp/Kconfig +++ b/net/mptcp/Kconfig @@ -4,7 +4,7 @@ config MPTCP depends on INET select SKB_EXTENSIONS select CRYPTO_LIB_SHA256 - select CRYPTO + select CRYPTO_LIB_UTILS help Multipath TCP (MPTCP) connections send and receive data over multiple subflows in order to utilize multiple network paths. Each subflow From e56cadaa27fd156106c5583ed98976927c6febc9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 16:47:40 -0800 Subject: [PATCH 020/214] ynl: add regen hint to new headers Recent commit 68e83f347266 ("tools: ynl-gen: add regeneration comment") added a hint how to regenerate the code to the headers. Update the new headers from this release cycle to also include it. Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251207004740.1657799-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/energy_model.h | 1 + kernel/power/em_netlink_autogen.c | 1 + kernel/power/em_netlink_autogen.h | 1 + 3 files changed, 3 insertions(+) diff --git a/include/uapi/linux/energy_model.h b/include/uapi/linux/energy_model.h index 4ec4c0eabbbbc..0bcad967854ff 100644 --- a/include/uapi/linux/energy_model.h +++ b/include/uapi/linux/energy_model.h @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN uapi header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #ifndef _UAPI_LINUX_ENERGY_MODEL_H #define _UAPI_LINUX_ENERGY_MODEL_H diff --git a/kernel/power/em_netlink_autogen.c b/kernel/power/em_netlink_autogen.c index a7a09ab1d1c21..ceb3b2bb6ebe0 100644 --- a/kernel/power/em_netlink_autogen.c +++ b/kernel/power/em_netlink_autogen.c @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN kernel source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #include #include diff --git a/kernel/power/em_netlink_autogen.h b/kernel/power/em_netlink_autogen.h index 78ce609641f11..140ab548103ce 100644 --- a/kernel/power/em_netlink_autogen.h +++ b/kernel/power/em_netlink_autogen.h @@ -2,6 +2,7 @@ /* Do not edit directly, auto-generated from: */ /* Documentation/netlink/specs/em.yaml */ /* YNL-GEN kernel header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ #ifndef _LINUX_EM_GEN_H #define _LINUX_EM_GEN_H From db6b35cffe59c619ea3772b21d7c7c8a7b885dc1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 17:38:48 -0800 Subject: [PATCH 021/214] tools: ynl: fix build on systems with old kernel headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wireguard YNL conversion was missing the customary .deps entry. NIPA doesn't catch this but my CentOS 9 system complains: wireguard-user.c:72:10: error: ‘WGALLOWEDIP_A_FLAGS’ undeclared here wireguard-user.c:58:67: error: parameter 1 (‘value’) has incomplete type 58 | const char *wireguard_wgallowedip_flags_str(enum wgallowedip_flag value) | ~~~~~~~~~~~~~~~~~~~~~~^~~~~ And similarly does Ubuntu 22.04. One extra complication here is that we renamed the header guard, so we need to compat with both old and new guard define. Reviewed-by: Asbjørn Sloth Tønnesen Link: https://patch.msgid.link/20251207013848.1692990-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/net/ynl/Makefile.deps | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps index 865fd2e8519ed..08205f9fc5257 100644 --- a/tools/net/ynl/Makefile.deps +++ b/tools/net/ynl/Makefile.deps @@ -13,6 +13,7 @@ UAPI_PATH:=../../../../include/uapi/ # need the explicit -D matching what's in /usr, to avoid multiple definitions. get_hdr_inc=-D$(1) -include $(UAPI_PATH)/linux/$(2) +get_hdr_inc2=-D$(1) -D$(2) -include $(UAPI_PATH)/linux/$(3) CFLAGS_devlink:=$(call get_hdr_inc,_LINUX_DEVLINK_H_,devlink.h) CFLAGS_dpll:=$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h) @@ -48,3 +49,4 @@ CFLAGS_tc:= $(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \ $(call get_hdr_inc,_TC_SKBEDIT_H,tc_act/tc_skbedit.h) \ $(call get_hdr_inc,_TC_TUNNEL_KEY_H,tc_act/tc_tunnel_key.h) CFLAGS_tcp_metrics:=$(call get_hdr_inc,_LINUX_TCP_METRICS_H,tcp_metrics.h) +CFLAGS_wireguard:=$(call get_hdr_inc2,_LINUX_WIREGUARD_H,_WG_UAPI_WIREGUARD_H,wireguard.h) From 0ace3297a7301911e52d8195cb1006414897c859 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 5 Dec 2025 19:55:14 +0100 Subject: [PATCH 022/214] mptcp: pm: ignore unknown endpoint flags Before this patch, the kernel was saving any flags set by the userspace, even unknown ones. This doesn't cause critical issues because the kernel is only looking at specific ones. But on the other hand, endpoints dumps could tell the userspace some recent flags seem to be supported on older kernel versions. Instead, ignore all unknown flags when parsing them. By doing that, the userspace can continue to set unsupported flags, but it has a way to verify what is supported by the kernel. Note that it sounds better to continue accepting unsupported flags not to change the behaviour, but also that eases things on the userspace side by adding "optional" endpoint types only supported by newer kernel versions without having to deal with the different kernel versions. A note for the backports: there will be conflicts in mptcp.h on older versions not having the mentioned flags, the new line should still be added last, and the '5' needs to be adapted to have the same value as the last entry. Fixes: 01cacb00b35c ("mptcp: add netlink-based PM") Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251205-net-mptcp-misc-fixes-6-19-rc1-v1-1-9e4781a6c1b8@kernel.org Signed-off-by: Jakub Kicinski --- include/uapi/linux/mptcp.h | 1 + net/mptcp/pm_netlink.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h index 04eea6d1d0a9b..72a5d030154e1 100644 --- a/include/uapi/linux/mptcp.h +++ b/include/uapi/linux/mptcp.h @@ -40,6 +40,7 @@ #define MPTCP_PM_ADDR_FLAG_FULLMESH _BITUL(3) #define MPTCP_PM_ADDR_FLAG_IMPLICIT _BITUL(4) #define MPTCP_PM_ADDR_FLAG_LAMINAR _BITUL(5) +#define MPTCP_PM_ADDR_FLAGS_MASK GENMASK(5, 0) struct mptcp_info { __u8 mptcpi_subflows; diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index d5b383870f799..7aa42de9c47b5 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -119,7 +119,8 @@ int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info, } if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) - entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); + entry->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]) & + MPTCP_PM_ADDR_FLAGS_MASK; if (tb[MPTCP_PM_ADDR_ATTR_PORT]) entry->addr.port = htons(nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT])); From 29f4801e9c8dfd12bdcb33b61a6ac479c7162bd7 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 5 Dec 2025 19:55:15 +0100 Subject: [PATCH 023/214] selftests: mptcp: pm: ensure unknown flags are ignored This validates the previous commit: the userspace can set unknown flags -- the 7th bit is currently unused -- without errors, but only the supported ones are printed in the endpoints dumps. The 'Fixes' tag here below is the same as the one from the previous commit: this patch here is not fixing anything wrong in the selftests, but it validates the previous fix for an issue introduced by this commit ID. Fixes: 01cacb00b35c ("mptcp: add netlink-based PM") Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251205-net-mptcp-misc-fixes-6-19-rc1-v1-2-9e4781a6c1b8@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/pm_netlink.sh | 4 ++++ tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index ec6a875881919..123d9d7a0278c 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -192,6 +192,10 @@ check "show_endpoints" \ flush_endpoint check "show_endpoints" "" "flush addrs" +add_endpoint 10.0.1.1 flags unknown +check "show_endpoints" "$(format_endpoints "1,10.0.1.1")" "ignore unknown flags" +flush_endpoint + set_limits 9 1 2>/dev/null check "get_limits" "${default_limits}" "rcv addrs above hard limit" diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c index 65b374232ff5a..99eecccbf0c87 100644 --- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c +++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c @@ -24,6 +24,8 @@ #define IPPROTO_MPTCP 262 #endif +#define MPTCP_PM_ADDR_FLAG_UNKNOWN _BITUL(7) + static void syntax(char *argv[]) { fprintf(stderr, "%s add|ann|rem|csf|dsf|get|set|del|flush|dump|events|listen|accept []\n", argv[0]); @@ -836,6 +838,8 @@ int add_addr(int fd, int pm_family, int argc, char *argv[]) flags |= MPTCP_PM_ADDR_FLAG_BACKUP; else if (!strcmp(tok, "fullmesh")) flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; + else if (!strcmp(tok, "unknown")) + flags |= MPTCP_PM_ADDR_FLAG_UNKNOWN; else error(1, errno, "unknown flag %s", argv[arg]); @@ -1048,6 +1052,13 @@ static void print_addr(struct rtattr *attrs, int len) printf(","); } + if (flags & MPTCP_PM_ADDR_FLAG_UNKNOWN) { + printf("unknown"); + flags &= ~MPTCP_PM_ADDR_FLAG_UNKNOWN; + if (flags) + printf(","); + } + /* bump unknown flags, if any */ if (flags) printf("0x%x", flags); From 2ea6190f42d0416a4310e60a7fcb0b49fcbbd4fb Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 5 Dec 2025 19:55:16 +0100 Subject: [PATCH 024/214] mptcp: schedule rtx timer only after pushing data The MPTCP protocol usually schedule the retransmission timer only when there is some chances for such retransmissions to happen. With a notable exception: __mptcp_push_pending() currently schedule such timer unconditionally, potentially leading to unnecessary rtx timer expiration. The issue is present since the blamed commit below but become easily reproducible after commit 27b0e701d387 ("mptcp: drop bogus optimization in __mptcp_check_push()") Fixes: 33d41c9cd74c ("mptcp: more accurate timeout") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251205-net-mptcp-misc-fixes-6-19-rc1-v1-3-9e4781a6c1b8@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index e212c1374bd04..d8a7f70291645 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1623,7 +1623,7 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) struct mptcp_sendmsg_info info = { .flags = flags, }; - bool do_check_data_fin = false; + bool copied = false; int push_count = 1; while (mptcp_send_head(sk) && (push_count > 0)) { @@ -1665,7 +1665,7 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) push_count--; continue; } - do_check_data_fin = true; + copied = true; } } } @@ -1674,11 +1674,14 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) if (ssk) mptcp_push_release(ssk, &info); - /* ensure the rtx timer is running */ - if (!mptcp_rtx_timer_pending(sk)) - mptcp_reset_rtx_timer(sk); - if (do_check_data_fin) + /* Avoid scheduling the rtx timer if no data has been pushed; the timer + * will be updated on positive acks by __mptcp_cleanup_una(). + */ + if (copied) { + if (!mptcp_rtx_timer_pending(sk)) + mptcp_reset_rtx_timer(sk); mptcp_check_send_data_fin(sk); + } } static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool first) From ffb8c27b0539dd90262d1021488e7817fae57c42 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 5 Dec 2025 19:55:17 +0100 Subject: [PATCH 025/214] mptcp: avoid deadlock on fallback while reinjecting Jakub reported an MPTCP deadlock at fallback time: WARNING: possible recursive locking detected 6.18.0-rc7-virtme #1 Not tainted -------------------------------------------- mptcp_connect/20858 is trying to acquire lock: ff1100001da18b60 (&msk->fallback_lock){+.-.}-{3:3}, at: __mptcp_try_fallback+0xd8/0x280 but task is already holding lock: ff1100001da18b60 (&msk->fallback_lock){+.-.}-{3:3}, at: __mptcp_retrans+0x352/0xaa0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&msk->fallback_lock); lock(&msk->fallback_lock); *** DEADLOCK *** May be due to missing lock nesting notation 3 locks held by mptcp_connect/20858: #0: ff1100001da18290 (sk_lock-AF_INET){+.+.}-{0:0}, at: mptcp_sendmsg+0x114/0x1bc0 #1: ff1100001db40fd0 (k-sk_lock-AF_INET#2){+.+.}-{0:0}, at: __mptcp_retrans+0x2cb/0xaa0 #2: ff1100001da18b60 (&msk->fallback_lock){+.-.}-{3:3}, at: __mptcp_retrans+0x352/0xaa0 stack backtrace: CPU: 0 UID: 0 PID: 20858 Comm: mptcp_connect Not tainted 6.18.0-rc7-virtme #1 PREEMPT(full) Hardware name: Bochs, BIOS Bochs 01/01/2011 Call Trace: dump_stack_lvl+0x6f/0xa0 print_deadlock_bug.cold+0xc0/0xcd validate_chain+0x2ff/0x5f0 __lock_acquire+0x34c/0x740 lock_acquire.part.0+0xbc/0x260 _raw_spin_lock_bh+0x38/0x50 __mptcp_try_fallback+0xd8/0x280 mptcp_sendmsg_frag+0x16c2/0x3050 __mptcp_retrans+0x421/0xaa0 mptcp_release_cb+0x5aa/0xa70 release_sock+0xab/0x1d0 mptcp_sendmsg+0xd5b/0x1bc0 sock_write_iter+0x281/0x4d0 new_sync_write+0x3c5/0x6f0 vfs_write+0x65e/0xbb0 ksys_write+0x17e/0x200 do_syscall_64+0xbb/0xfd0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7fa5627cbc5e Code: 4d 89 d8 e8 14 bd 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 c3 83 e2 39 83 fa 08 75 e7 e8 13 ff ff ff 0f 1f 00 f3 0f 1e fa RSP: 002b:00007fff1fe14700 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000000000000005 RCX: 00007fa5627cbc5e RDX: 0000000000001f9c RSI: 00007fff1fe16984 RDI: 0000000000000005 RBP: 00007fff1fe14710 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000202 R12: 00007fff1fe16920 R13: 0000000000002000 R14: 0000000000001f9c R15: 0000000000001f9c The packet scheduler could attempt a reinjection after receiving an MP_FAIL and before the infinite map has been transmitted, causing a deadlock since MPTCP needs to do the reinjection atomically from WRT fallback. Address the issue explicitly avoiding the reinjection in the critical scenario. Note that this is the only fallback critical section that could potentially send packets and hit the double-lock. Reported-by: Jakub Kicinski Closes: https://netdev-ctrl.bots.linux.dev/logs/vmksft/mptcp-dbg/results/412720/1-mptcp-join-sh/stderr Fixes: f8a1d9b18c5e ("mptcp: make fallback action and fallback decision atomic") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251205-net-mptcp-misc-fixes-6-19-rc1-v1-4-9e4781a6c1b8@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index d8a7f70291645..9b1fafd87cb94 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2769,10 +2769,13 @@ static void __mptcp_retrans(struct sock *sk) /* * make the whole retrans decision, xmit, disallow - * fallback atomic + * fallback atomic, note that we can't retrans even + * when an infinite fallback is in progress, i.e. new + * subflows are disallowed. */ spin_lock_bh(&msk->fallback_lock); - if (__mptcp_check_fallback(msk)) { + if (__mptcp_check_fallback(msk) || + !msk->allow_subflows) { spin_unlock_bh(&msk->fallback_lock); release_sock(ssk); goto clear_scheduled; From 6abd4577bccc66f83edfdb24dc484723ae99cbe8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 11:00:09 +0100 Subject: [PATCH 026/214] can: fix build dependency A recent bugfix introduced a new problem with Kconfig dependencies: WARNING: unmet direct dependencies detected for CAN_DEV Depends on [n]: NETDEVICES [=n] && CAN [=m] Selected by [m]: - CAN [=m] && NET [=y] Since the CAN core code now links into the CAN device code, that particular function needs to be available, though the rest of it does not. Revert the incomplete fix and instead use Makefile logic to avoid the link failure. Fixes: cb2dc6d2869a ("can: Kconfig: select CAN driver infrastructure by default") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512091523.zty3CLmc-lkp@intel.com/ Signed-off-by: Arnd Bergmann Tested-by: Oliver Hartkopp Acked-by: Oliver Hartkopp Link: https://patch.msgid.link/20251204100015.1033688-1-arnd@kernel.org [mkl: removed module option from CAN_DEV help text (thanks Vincent)] [mkl: removed '&& CAN' from Kconfig dependency (thanks Vincent)] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 5 +---- drivers/net/can/Makefile | 2 +- drivers/net/can/dev/Makefile | 5 ++--- net/can/Kconfig | 1 - 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e15e320db4763..460a74ae69233 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig CAN_DEV - tristate "CAN Device Drivers" + bool "CAN Device Drivers" default y depends on CAN help @@ -17,9 +17,6 @@ menuconfig CAN_DEV virtual ones. If you own such devices or plan to use the virtual CAN interfaces to develop applications, say Y here. - To compile as a module, choose M here: the module will be called - can-dev. - if CAN_DEV config CAN_VCAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index d7bc10a6b8eae..37e2f1a2faecd 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_VXCAN) += vxcan.o obj-$(CONFIG_CAN_SLCAN) += slcan/ -obj-y += dev/ +obj-$(CONFIG_CAN_DEV) += dev/ obj-y += esd/ obj-y += rcar/ obj-y += rockchip/ diff --git a/drivers/net/can/dev/Makefile b/drivers/net/can/dev/Makefile index 633687d6b6c0c..64226acf0f3d4 100644 --- a/drivers/net/can/dev/Makefile +++ b/drivers/net/can/dev/Makefile @@ -1,9 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_CAN_DEV) += can-dev.o - -can-dev-y += skb.o +obj-$(CONFIG_CAN) += can-dev.o +can-dev-$(CONFIG_CAN_DEV) += skb.o can-dev-$(CONFIG_CAN_CALC_BITTIMING) += calc_bittiming.o can-dev-$(CONFIG_CAN_NETLINK) += bittiming.o can-dev-$(CONFIG_CAN_NETLINK) += dev.o diff --git a/net/can/Kconfig b/net/can/Kconfig index e4ccf731a24ce..af64a6f764588 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -5,7 +5,6 @@ menuconfig CAN tristate "CAN bus subsystem support" - select CAN_DEV help Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial communications protocol. Development of the CAN bus started in From 3e54d3b4a8437b6783d4145c86962a2aa51022f3 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 1 Dec 2025 19:26:38 +0100 Subject: [PATCH 027/214] can: gs_usb: gs_can_open(): fix error handling Commit 2603be9e8167 ("can: gs_usb: gs_can_open(): improve error handling") added missing error handling to the gs_can_open() function. The driver uses 2 USB anchors to track the allocated URBs: the TX URBs in struct gs_can::tx_submitted for each netdev and the RX URBs in struct gs_usb::rx_submitted for the USB device. gs_can_open() allocates the RX URBs, while TX URBs are allocated during gs_can_start_xmit(). The cleanup in gs_can_open() kills all anchored dev->tx_submitted URBs (which is not necessary since the netdev is not yet registered), but misses the parent->rx_submitted URBs. Fix the problem by killing the rx_submitted instead of the tx_submitted. Fixes: 2603be9e8167 ("can: gs_usb: gs_can_open(): improve error handling") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20251210-gs_usb-fix-error-handling-v1-1-d6a5a03f10bb@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index e29e85b67fd40..a0233e550a5ad 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -1074,7 +1074,7 @@ static int gs_can_open(struct net_device *netdev) usb_free_urb(urb); out_usb_kill_anchored_urbs: if (!parent->active_channels) { - usb_kill_anchored_urbs(&dev->tx_submitted); + usb_kill_anchored_urbs(&parent->rx_submitted); if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) gs_usb_timestamp_stop(parent); From 5ace7ef87f059d68b5f50837ef3e8a1a4870c36e Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Thu, 4 Dec 2025 11:53:32 +0100 Subject: [PATCH 028/214] net: openvswitch: fix middle attribute validation in push_nsh() action The push_nsh() action structure looks like this: OVS_ACTION_ATTR_PUSH_NSH(OVS_KEY_ATTR_NSH(OVS_NSH_KEY_ATTR_BASE,...)) The outermost OVS_ACTION_ATTR_PUSH_NSH attribute is OK'ed by the nla_for_each_nested() inside __ovs_nla_copy_actions(). The innermost OVS_NSH_KEY_ATTR_BASE/MD1/MD2 are OK'ed by the nla_for_each_nested() inside nsh_key_put_from_nlattr(). But nothing checks if the attribute in the middle is OK. We don't even check that this attribute is the OVS_KEY_ATTR_NSH. We just do a double unwrap with a pair of nla_data() calls - first time directly while calling validate_push_nsh() and the second time as part of the nla_for_each_nested() macro, which isn't safe, potentially causing invalid memory access if the size of this attribute is incorrect. The failure may not be noticed during validation due to larger netlink buffer, but cause trouble later during action execution where the buffer is allocated exactly to the size: BUG: KASAN: slab-out-of-bounds in nsh_hdr_from_nlattr+0x1dd/0x6a0 [openvswitch] Read of size 184 at addr ffff88816459a634 by task a.out/22624 CPU: 8 UID: 0 PID: 22624 6.18.0-rc7+ #115 PREEMPT(voluntary) Call Trace: dump_stack_lvl+0x51/0x70 print_address_description.constprop.0+0x2c/0x390 kasan_report+0xdd/0x110 kasan_check_range+0x35/0x1b0 __asan_memcpy+0x20/0x60 nsh_hdr_from_nlattr+0x1dd/0x6a0 [openvswitch] push_nsh+0x82/0x120 [openvswitch] do_execute_actions+0x1405/0x2840 [openvswitch] ovs_execute_actions+0xd5/0x3b0 [openvswitch] ovs_packet_cmd_execute+0x949/0xdb0 [openvswitch] genl_family_rcv_msg_doit+0x1d6/0x2b0 genl_family_rcv_msg+0x336/0x580 genl_rcv_msg+0x9f/0x130 netlink_rcv_skb+0x11f/0x370 genl_rcv+0x24/0x40 netlink_unicast+0x73e/0xaa0 netlink_sendmsg+0x744/0xbf0 __sys_sendto+0x3d6/0x450 do_syscall_64+0x79/0x2c0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Let's add some checks that the attribute is properly sized and it's the only one attribute inside the action. Technically, there is no real reason for OVS_KEY_ATTR_NSH to be there, as we know that we're pushing an NSH header already, it just creates extra nesting, but that's how uAPI works today. So, keeping as it is. Fixes: b2d0f5d5dc53 ("openvswitch: enable NSH support") Reported-by: Junvy Yang Signed-off-by: Ilya Maximets Acked-by: Eelco Chaudron echaudro@redhat.com Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20251204105334.900379-1-i.maximets@ovn.org Signed-off-by: Jakub Kicinski --- net/openvswitch/flow_netlink.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 1cb4f97335d87..2d536901309ea 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2802,13 +2802,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, return err; } -static bool validate_push_nsh(const struct nlattr *attr, bool log) +static bool validate_push_nsh(const struct nlattr *a, bool log) { + struct nlattr *nsh_key = nla_data(a); struct sw_flow_match match; struct sw_flow_key key; + /* There must be one and only one NSH header. */ + if (!nla_ok(nsh_key, nla_len(a)) || + nla_total_size(nla_len(nsh_key)) != nla_len(a) || + nla_type(nsh_key) != OVS_KEY_ATTR_NSH) + return false; + ovs_match_init(&match, &key, true, NULL); - return !nsh_key_put_from_nlattr(attr, &match, false, true, log); + return !nsh_key_put_from_nlattr(nsh_key, &match, false, true, log); } /* Return false if there are any non-masked bits set. @@ -3389,7 +3396,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, return -EINVAL; } mac_proto = MAC_PROTO_NONE; - if (!validate_push_nsh(nla_data(a), log)) + if (!validate_push_nsh(a, log)) return -EINVAL; break; From 9e7477a427449a8a3cd00c188e20a880e3d94638 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 11:01:28 +0100 Subject: [PATCH 029/214] net: ti: icssg-prueth: add PTP_1588_CLOCK_OPTIONAL dependency The new icssg-prueth driver needs the same dependency as the other parts that use the ptp-1588: WARNING: unmet direct dependencies detected for TI_ICSS_IEP Depends on [m]: NETDEVICES [=y] && ETHERNET [=y] && NET_VENDOR_TI [=y] && PTP_1588_CLOCK_OPTIONAL [=m] && TI_PRUSS [=y] Selected by [y]: - TI_PRUETH [=y] && NETDEVICES [=y] && ETHERNET [=y] && NET_VENDOR_TI [=y] && PRU_REMOTEPROC [=y] && NET_SWITCHDEV [=y] Add the correct dependency on the two drivers missing it, and remove the pointless 'imply' in the process. Fixes: e654b85a693e ("net: ti: icssg-prueth: Add ICSSG Ethernet driver for AM65x SR1.0 platforms") Fixes: 511f6c1ae093 ("net: ti: icssm-prueth: Adds ICSSM Ethernet driver") Signed-off-by: Arnd Bergmann Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20251204100138.1034175-1-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index a54d71155263c..fe5b2926d8ab0 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -209,6 +209,7 @@ config TI_ICSSG_PRUETH_SR1 depends on PRU_REMOTEPROC depends on NET_SWITCHDEV depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER + depends on PTP_1588_CLOCK_OPTIONAL help Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem. This subsystem is available on the AM65 SR1.0 platform. @@ -234,7 +235,7 @@ config TI_PRUETH depends on PRU_REMOTEPROC depends on NET_SWITCHDEV select TI_ICSS_IEP - imply PTP_1588_CLOCK + depends on PTP_1588_CLOCK_OPTIONAL help Some TI SoCs has Programmable Realtime Unit (PRU) cores which can support Single or Dual Ethernet ports with the help of firmware code From 6af2a01d65f89e73c1cbb9267f8880d83a88cee4 Mon Sep 17 00:00:00 2001 From: caoping Date: Thu, 4 Dec 2025 01:10:58 -0800 Subject: [PATCH 030/214] net/handshake: restore destructor on submit failure handshake_req_submit() replaces sk->sk_destruct but never restores it when submission fails before the request is hashed. handshake_sk_destruct() then returns early and the original destructor never runs, leaking the socket. Restore sk_destruct on the error path. Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") Reviewed-by: Chuck Lever Cc: stable@vger.kernel.org Signed-off-by: caoping Link: https://patch.msgid.link/20251204091058.1545151-1-caoping@cmss.chinamobile.com Signed-off-by: Jakub Kicinski --- net/handshake/request.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/handshake/request.c b/net/handshake/request.c index 274d2c89b6b20..89435ed755cd0 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -276,6 +276,8 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, out_unlock: spin_unlock(&hn->hn_lock); out_err: + /* Restore original destructor so socket teardown still runs on failure */ + req->hr_sk->sk_destruct = req->hr_odestruct; trace_handshake_submit_err(net, req, req->hr_sk, ret); handshake_req_destroy(req); return ret; From 50b3db3e11864cb4e18ff099cfb38e11e7f87a68 Mon Sep 17 00:00:00 2001 From: Alexey Simakov Date: Fri, 5 Dec 2025 18:58:16 +0300 Subject: [PATCH 031/214] broadcom: b44: prevent uninitialized value usage On execution path with raised B44_FLAG_EXTERNAL_PHY, b44_readphy() leaves bmcr value uninitialized and it is used later in the code. Add check of this flag at the beginning of the b44_nway_reset() and exit early of the function with restarting autonegotiation if an external PHY is used. Fixes: 753f492093da ("[B44]: port to native ssb support") Reviewed-by: Jonas Gorski Reviewed-by: Andrew Lunn Signed-off-by: Alexey Simakov Reviewed-by: Michael Chan Link: https://patch.msgid.link/20251205155815.4348-1-bigalex934@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/b44.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 888f28f11406f..90df02e0039cb 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1790,6 +1790,9 @@ static int b44_nway_reset(struct net_device *dev) u32 bmcr; int r; + if (bp->flags & B44_FLAG_EXTERNAL_PHY) + return phy_ethtool_nway_reset(dev); + spin_lock_irq(&bp->lock); b44_readphy(bp, MII_BMCR, &bmcr); b44_readphy(bp, MII_BMCR, &bmcr); From 9580f6d47dd6156c6d16e988d28faa74e5a0b8ba Mon Sep 17 00:00:00 2001 From: Ankit Khushwaha Date: Fri, 5 Dec 2025 22:02:42 +0530 Subject: [PATCH 032/214] selftests: tls: fix warning of uninitialized variable In 'poll_partial_rec_async' a uninitialized char variable 'token' with is used for write/read instruction to synchronize between threads via a pipe. tls.c:2833:26: warning: variable 'token' is uninitialized when passed as a const pointer argument Initialize 'token' to '\0' to silence compiler warning. Signed-off-by: Ankit Khushwaha Link: https://patch.msgid.link/20251205163242.14615-1-ankitkhushwaha.linux@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index da1b50b307194..a625d0be62d0e 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -2786,10 +2786,10 @@ TEST_F(tls_err, epoll_partial_rec) TEST_F(tls_err, poll_partial_rec_async) { struct pollfd pfd = { }; + char token = '\0'; ssize_t rec_len; char rec[256]; char buf[128]; - char token; int p[2]; int ret; From 06f7cae92fe346fa49a8a9b161124b26cc5c3ed1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Dec 2025 09:10:00 -0800 Subject: [PATCH 033/214] selftest: af_unix: Support compilers without flex-array-member-not-at-end support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: gcc: error: unrecognized command-line option ‘-Wflex-array-member-not-at-end’ by making the compiler option dependent on its support. Fixes: 1838731f1072c ("selftest: af_unix: Add -Wall and -Wflex-array-member-not-at-end to CFLAGS.") Cc: Kuniyuki Iwashima Signed-off-by: Guenter Roeck Link: https://patch.msgid.link/20251205171010.515236-7-linux@roeck-us.net Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/af_unix/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 3cd677b720728..4c0375e28bbee 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -1,4 +1,9 @@ -CFLAGS += $(KHDR_INCLUDES) -Wall -Wflex-array-member-not-at-end +top_srcdir := ../../../../.. +include $(top_srcdir)/scripts/Makefile.compiler + +cc-option = $(call __cc-option, $(CC),,$(1),$(2)) + +CFLAGS += $(KHDR_INCLUDES) -Wall $(call cc-option,-Wflex-array-member-not-at-end) TEST_GEN_PROGS := \ diag_uid \ From 59546e874403c1dd0cbc42df06fdf8c113f72022 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Dec 2025 09:10:04 -0800 Subject: [PATCH 034/214] selftests: net: Fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix ksft.h: In function ‘ksft_ready’: ksft.h:27:9: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ ksft.h: In function ‘ksft_wait’: ksft.h:51:9: warning: ignoring return value of ‘read’ declared with attribute ‘warn_unused_result’ by checking the return value of the affected functions and displaying an error message if an error is seen. Fixes: 2b6d490b82668 ("selftests: drv-net: Factor out ksft C helpers") Cc: Joe Damato Signed-off-by: Guenter Roeck Link: https://patch.msgid.link/20251205171010.515236-11-linux@roeck-us.net Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/lib/ksft.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/lib/ksft.h b/tools/testing/selftests/net/lib/ksft.h index 17dc34a612c64..03912902a6d30 100644 --- a/tools/testing/selftests/net/lib/ksft.h +++ b/tools/testing/selftests/net/lib/ksft.h @@ -24,7 +24,8 @@ static inline void ksft_ready(void) fd = STDOUT_FILENO; } - write(fd, msg, sizeof(msg)); + if (write(fd, msg, sizeof(msg)) < 0) + perror("write()"); if (fd != STDOUT_FILENO) close(fd); } @@ -48,7 +49,8 @@ static inline void ksft_wait(void) fd = STDIN_FILENO; } - read(fd, &byte, sizeof(byte)); + if (read(fd, &byte, sizeof(byte)) < 0) + perror("read()"); if (fd != STDIN_FILENO) close(fd); } From 91dc09a609d9443e6b34bdb355a18d579a95e132 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Dec 2025 09:10:07 -0800 Subject: [PATCH 035/214] selftests: net: tfo: Fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix tfo.c: In function ‘run_server’: tfo.c:84:9: warning: ignoring return value of ‘read’ declared with attribute ‘warn_unused_result’ by evaluating the return value from read() and displaying an error message if it reports an error. Fixes: c65b5bb2329e3 ("selftests: net: add passive TFO test binary") Cc: David Wei Signed-off-by: Guenter Roeck Link: https://patch.msgid.link/20251205171010.515236-14-linux@roeck-us.net Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tfo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/tfo.c b/tools/testing/selftests/net/tfo.c index eb3cac5e583c9..8d82140f0f767 100644 --- a/tools/testing/selftests/net/tfo.c +++ b/tools/testing/selftests/net/tfo.c @@ -81,7 +81,8 @@ static void run_server(void) if (getsockopt(connfd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &opt, &len) < 0) error(1, errno, "getsockopt(SO_INCOMING_NAPI_ID)"); - read(connfd, buf, 64); + if (read(connfd, buf, 64) < 0) + perror("read()"); fprintf(outfile, "%d\n", opt); fclose(outfile); From 8ef522c8a59a048117f7e05eb5213043c02f986f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 17:09:39 -0800 Subject: [PATCH 036/214] inet: frags: avoid theoretical race in ip_frag_reinit() In ip_frag_reinit() we want to move the frag timeout timer into the future. If the timer fires in the meantime we inadvertently scheduled it again, and since the timer assumes a ref on frag_queue we need to acquire one to balance things out. This is technically racy, we should have acquired the reference _before_ we touch the timer, it may fire again before we take the ref. Avoid this entire dance by using mod_timer_pending() which only modifies the timer if its pending (and which exists since Linux v2.6.30) Note that this was the only place we ever took a ref on frag_queue since Eric's conversion to RCU. So we could potentially replace the whole refcnt field with an atomic flag and a bit more RCU. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20251207010942.1672972-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/ipv4/inet_fragment.c | 4 +++- net/ipv4/ip_fragment.c | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 025895eb6ec59..30f4fa50ee2d7 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -327,7 +327,9 @@ static struct inet_frag_queue *inet_frag_alloc(struct fqdir *fqdir, timer_setup(&q->timer, f->frag_expire, 0); spin_lock_init(&q->lock); - /* One reference for the timer, one for the hash table. */ + /* One reference for the timer, one for the hash table. + * We never take any extra references, only decrement this field. + */ refcount_set(&q->refcnt, 2); return q; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index f7012479713ba..d7bccdc9dc693 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -242,10 +242,8 @@ static int ip_frag_reinit(struct ipq *qp) { unsigned int sum_truesize = 0; - if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { - refcount_inc(&qp->q.refcnt); + if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) return -ETIMEDOUT; - } sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, SKB_DROP_REASON_FRAG_TOO_FAR); From 1231eec6994be29d6bb5c303dfa54731ed9fc0e6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 17:09:40 -0800 Subject: [PATCH 037/214] inet: frags: add inet_frag_queue_flush() Instead of exporting inet_frag_rbtree_purge() which requires that caller takes care of memory accounting, add a new helper. We will need to call it from a few places in the next patch. Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/net/inet_frag.h | 5 ++--- net/ipv4/inet_fragment.c | 15 ++++++++++++--- net/ipv4/ip_fragment.c | 6 +----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 0eccd9c3a883f..3ffaceee7bbc0 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q, int *refs); void inet_frag_destroy(struct inet_frag_queue *q); struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); -/* Free all skbs in the queue; return the sum of their truesizes. */ -unsigned int inet_frag_rbtree_purge(struct rb_root *root, - enum skb_drop_reason reason); +void inet_frag_queue_flush(struct inet_frag_queue *q, + enum skb_drop_reason reason); static inline void inet_frag_putn(struct inet_frag_queue *q, int refs) { diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 30f4fa50ee2d7..1bf969b5a1cb5 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -263,8 +263,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) kmem_cache_free(f->frags_cachep, q); } -unsigned int inet_frag_rbtree_purge(struct rb_root *root, - enum skb_drop_reason reason) +static unsigned int +inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason) { struct rb_node *p = rb_first(root); unsigned int sum = 0; @@ -284,7 +284,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root, } return sum; } -EXPORT_SYMBOL(inet_frag_rbtree_purge); + +void inet_frag_queue_flush(struct inet_frag_queue *q, + enum skb_drop_reason reason) +{ + unsigned int sum; + + sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); + sub_frag_mem_limit(q->fqdir, sum); +} +EXPORT_SYMBOL(inet_frag_queue_flush); void inet_frag_destroy(struct inet_frag_queue *q) { diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index d7bccdc9dc693..32f1c1a46ba72 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -240,14 +240,10 @@ static int ip_frag_too_far(struct ipq *qp) static int ip_frag_reinit(struct ipq *qp) { - unsigned int sum_truesize = 0; - if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) return -ETIMEDOUT; - sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, - SKB_DROP_REASON_FRAG_TOO_FAR); - sub_frag_mem_limit(qp->q.fqdir, sum_truesize); + inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR); qp->q.flags = 0; qp->q.len = 0; From 006a5035b495dec008805df249f92c22c89c3d2e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 17:09:41 -0800 Subject: [PATCH 038/214] inet: frags: flush pending skbs in fqdir_pre_exit() We have been seeing occasional deadlocks on pernet_ops_rwsem since September in NIPA. The stuck task was usually modprobe (often loading a driver like ipvlan), trying to take the lock as a Writer. lockdep does not track readers for rwsems so the read wasn't obvious from the reports. On closer inspection the Reader holding the lock was conntrack looping forever in nf_conntrack_cleanup_net_list(). Based on past experience with occasional NIPA crashes I looked thru the tests which run before the crash and noticed that the crash follows ip_defrag.sh. An immediate red flag. Scouring thru (de)fragmentation queues reveals skbs sitting around, holding conntrack references. The problem is that since conntrack depends on nf_defrag_ipv6, nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its netns exit hooks run _after_ conntrack's netns exit hook. Flush all fragment queue SKBs during fqdir_pre_exit() to release conntrack references before conntrack cleanup runs. Also flush the queues in timer expiry handlers when they discover fqdir->dead is set, in case packet sneaks in while we're running the pre_exit flush. The commit under Fixes is not exactly the culprit, but I think previously the timer firing would eventually unblock the spinning conntrack. Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units") Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/net/inet_frag.h | 13 +------------ include/net/ipv6_frag.h | 9 ++++++--- net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++ net/ipv4/ip_fragment.c | 12 +++++++----- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 3ffaceee7bbc0..365925c9d2628 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *); int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net); -static inline void fqdir_pre_exit(struct fqdir *fqdir) -{ - /* Prevent creation of new frags. - * Pairs with READ_ONCE() in inet_frag_find(). - */ - WRITE_ONCE(fqdir->high_thresh, 0); - - /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() - * and ip6frag_expire_frag_queue(). - */ - WRITE_ONCE(fqdir->dead, true); -} +void fqdir_pre_exit(struct fqdir *fqdir); void fqdir_exit(struct fqdir *fqdir); void inet_frag_kill(struct inet_frag_queue *q, int *refs); diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h index 38ef66826939e..41d9fc6965f9a 100644 --- a/include/net/ipv6_frag.h +++ b/include/net/ipv6_frag.h @@ -69,9 +69,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) int refs = 1; rcu_read_lock(); - /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ - if (READ_ONCE(fq->q.fqdir->dead)) - goto out_rcu_unlock; spin_lock(&fq->q.lock); if (fq->q.flags & INET_FRAG_COMPLETE) @@ -80,6 +77,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) fq->q.flags |= INET_FRAG_DROP; inet_frag_kill(&fq->q, &refs); + /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ + if (READ_ONCE(fq->q.fqdir->dead)) { + inet_frag_queue_flush(&fq->q, 0); + goto out; + } + dev = dev_get_by_index_rcu(net, fq->iif); if (!dev) goto out; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 1bf969b5a1cb5..001ee5c4d962e 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -218,6 +218,41 @@ static int __init inet_frag_wq_init(void) pure_initcall(inet_frag_wq_init); +void fqdir_pre_exit(struct fqdir *fqdir) +{ + struct inet_frag_queue *fq; + struct rhashtable_iter hti; + + /* Prevent creation of new frags. + * Pairs with READ_ONCE() in inet_frag_find(). + */ + WRITE_ONCE(fqdir->high_thresh, 0); + + /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() + * and ip6frag_expire_frag_queue(). + */ + WRITE_ONCE(fqdir->dead, true); + + rhashtable_walk_enter(&fqdir->rhashtable, &hti); + rhashtable_walk_start(&hti); + + while ((fq = rhashtable_walk_next(&hti))) { + if (IS_ERR(fq)) { + if (PTR_ERR(fq) != -EAGAIN) + break; + continue; + } + spin_lock_bh(&fq->lock); + if (!(fq->flags & INET_FRAG_COMPLETE)) + inet_frag_queue_flush(fq, 0); + spin_unlock_bh(&fq->lock); + } + + rhashtable_walk_stop(&hti); + rhashtable_walk_exit(&hti); +} +EXPORT_SYMBOL(fqdir_pre_exit); + void fqdir_exit(struct fqdir *fqdir) { INIT_WORK(&fqdir->destroy_work, fqdir_work_fn); @@ -290,6 +325,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, { unsigned int sum; + reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); sub_frag_mem_limit(q->fqdir, sum); } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 32f1c1a46ba72..56b0f738d2f27 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -134,11 +134,6 @@ static void ip_expire(struct timer_list *t) net = qp->q.fqdir->net; rcu_read_lock(); - - /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ - if (READ_ONCE(qp->q.fqdir->dead)) - goto out_rcu_unlock; - spin_lock(&qp->q.lock); if (qp->q.flags & INET_FRAG_COMPLETE) @@ -146,6 +141,13 @@ static void ip_expire(struct timer_list *t) qp->q.flags |= INET_FRAG_DROP; inet_frag_kill(&qp->q, &refs); + + /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ + if (READ_ONCE(qp->q.fqdir->dead)) { + inet_frag_queue_flush(&qp->q, 0); + goto out; + } + __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); From 92df4c56cf5b739c2977001c581badeaf82b9857 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 6 Dec 2025 17:09:42 -0800 Subject: [PATCH 039/214] netfilter: conntrack: warn when cleanup is stuck nf_conntrack_cleanup_net_list() calls schedule() so it does not show up as a hung task. Add an explicit check to make debugging leaked skbs/conntack references more obvious. Acked-by: Florian Westphal Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20251207010942.1672972-5-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/netfilter/nf_conntrack_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0b95f226f2111..d1f8eb725d422 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2487,6 +2487,7 @@ void nf_conntrack_cleanup_net(struct net *net) void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) { struct nf_ct_iter_data iter_data = {}; + unsigned long start = jiffies; struct net *net; int busy; @@ -2507,6 +2508,8 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) busy = 1; } if (busy) { + DEBUG_NET_WARN_ONCE(time_after(jiffies, start + 60 * HZ), + "conntrack cleanup blocked for 60s"); schedule(); goto i_see_dead_people; } From 2e2a720766886190a6d35c116794693aabd332b6 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Fri, 5 Dec 2025 12:58:01 +0100 Subject: [PATCH 040/214] netfilter: nf_conncount: fix leaked ct in error paths There are some situations where ct might be leaked as error paths are skipping the refcounted check and return immediately. In order to solve it make sure that the check is always called. Fixes: be102eb6a0e7 ("netfilter: nf_conncount: rework API to use sk_buff directly") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal --- net/netfilter/nf_conncount.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index f1be4dd5cf85f..3654f1e8976c9 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -172,14 +172,14 @@ static int __nf_conncount_add(struct net *net, struct nf_conn *found_ct; unsigned int collect = 0; bool refcounted = false; + int err = 0; if (!get_ct_or_tuple_from_skb(net, skb, l3num, &ct, &tuple, &zone, &refcounted)) return -ENOENT; if (ct && nf_ct_is_confirmed(ct)) { - if (refcounted) - nf_ct_put(ct); - return -EEXIST; + err = -EEXIST; + goto out_put; } if ((u32)jiffies == list->last_gc) @@ -231,12 +231,16 @@ static int __nf_conncount_add(struct net *net, } add_new_node: - if (WARN_ON_ONCE(list->count > INT_MAX)) - return -EOVERFLOW; + if (WARN_ON_ONCE(list->count > INT_MAX)) { + err = -EOVERFLOW; + goto out_put; + } conn = kmem_cache_alloc(conncount_conn_cachep, GFP_ATOMIC); - if (conn == NULL) - return -ENOMEM; + if (conn == NULL) { + err = -ENOMEM; + goto out_put; + } conn->tuple = tuple; conn->zone = *zone; @@ -249,7 +253,7 @@ static int __nf_conncount_add(struct net *net, out_put: if (refcounted) nf_ct_put(ct); - return 0; + return err; } int nf_conncount_add_skb(struct net *net, @@ -456,11 +460,10 @@ insert_tree(struct net *net, rb_link_node_rcu(&rbconn->node, parent, rbnode); rb_insert_color(&rbconn->node, root); - - if (refcounted) - nf_ct_put(ct); } out_unlock: + if (refcounted) + nf_ct_put(ct); spin_unlock_bh(&nf_conncount_locks[hash]); return count; } From ad891bb3d079a46a821bf2b8867854645191bab0 Mon Sep 17 00:00:00 2001 From: Slavin Liu Date: Fri, 21 Nov 2025 16:52:13 +0800 Subject: [PATCH 041/214] ipvs: fix ipv4 null-ptr-deref in route error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IPv4 code path in __ip_vs_get_out_rt() calls dst_link_failure() without ensuring skb->dev is set, leading to a NULL pointer dereference in fib_compute_spec_dst() when ipv4_link_failure() attempts to send ICMP destination unreachable messages. The issue emerged after commit ed0de45a1008 ("ipv4: recompile ip options in ipv4_link_failure") started calling __ip_options_compile() from ipv4_link_failure(). This code path eventually calls fib_compute_spec_dst() which dereferences skb->dev. An attempt was made to fix the NULL skb->dev dereference in commit 0113d9c9d1cc ("ipv4: fix null-deref in ipv4_link_failure"), but it only addressed the immediate dev_net(skb->dev) dereference by using a fallback device. The fix was incomplete because fib_compute_spec_dst() later in the call chain still accesses skb->dev directly, which remains NULL when IPVS calls dst_link_failure(). The crash occurs when: 1. IPVS processes a packet in NAT mode with a misconfigured destination 2. Route lookup fails in __ip_vs_get_out_rt() before establishing a route 3. The error path calls dst_link_failure(skb) with skb->dev == NULL 4. ipv4_link_failure() → ipv4_send_dest_unreach() → __ip_options_compile() → fib_compute_spec_dst() 5. fib_compute_spec_dst() dereferences NULL skb->dev Apply the same fix used for IPv6 in commit 326bf17ea5d4 ("ipvs: fix ipv6 route unreach panic"): set skb->dev from skb_dst(skb)->dev before calling dst_link_failure(). KASAN: null-ptr-deref in range [0x0000000000000328-0x000000000000032f] CPU: 1 PID: 12732 Comm: syz.1.3469 Not tainted 6.6.114 #2 RIP: 0010:__in_dev_get_rcu include/linux/inetdevice.h:233 RIP: 0010:fib_compute_spec_dst+0x17a/0x9f0 net/ipv4/fib_frontend.c:285 Call Trace: spec_dst_fill net/ipv4/ip_options.c:232 spec_dst_fill net/ipv4/ip_options.c:229 __ip_options_compile+0x13a1/0x17d0 net/ipv4/ip_options.c:330 ipv4_send_dest_unreach net/ipv4/route.c:1252 ipv4_link_failure+0x702/0xb80 net/ipv4/route.c:1265 dst_link_failure include/net/dst.h:437 __ip_vs_get_out_rt+0x15fd/0x19e0 net/netfilter/ipvs/ip_vs_xmit.c:412 ip_vs_nat_xmit+0x1d8/0xc80 net/netfilter/ipvs/ip_vs_xmit.c:764 Fixes: ed0de45a1008 ("ipv4: recompile ip options in ipv4_link_failure") Signed-off-by: Slavin Liu Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_xmit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 3162ce3c26404..64c697212578a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -408,6 +408,9 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, return -1; err_unreach: + if (!skb->dev) + skb->dev = skb_dst(skb)->dev; + dst_link_failure(skb); return -1; } From 2bdc536c9da7fa08baf0fafe9d91243b83cb9c8b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 1 Dec 2025 11:22:45 +0100 Subject: [PATCH 042/214] netfilter: always set route tuple out ifindex Always set nf_flow_route tuple out ifindex even if the indev is not one of the flowtable configured devices since otherwise the outdev lookup in nf_flow_offload_ip_hook() or nf_flow_offload_ipv6_hook() for FLOW_OFFLOAD_XMIT_NEIGH flowtable entries will fail. The above issue occurs in the following configuration since IP6IP6 tunnel does not support flowtable acceleration yet: $ip addr show 5: eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:11:22:33:22:55 brd ff:ff:ff:ff:ff:ff link-netns ns1 inet6 2001:db8:1::2/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::211:22ff:fe33:2255/64 scope link tentative proto kernel_ll valid_lft forever preferred_lft forever 6: eth1: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:22:22:33:22:55 brd ff:ff:ff:ff:ff:ff link-netns ns3 inet6 2001:db8:2::1/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::222:22ff:fe33:2255/64 scope link tentative proto kernel_ll valid_lft forever preferred_lft forever 7: tun0@NONE: mtu 1452 qdisc noqueue state UNKNOWN group default qlen 1000 link/tunnel6 2001:db8:2::1 peer 2001:db8:2::2 permaddr a85:e732:2c37:: inet6 2002:db8:1::1/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::885:e7ff:fe32:2c37/64 scope link proto kernel_ll valid_lft forever preferred_lft forever $ip -6 route show 2001:db8:1::/64 dev eth0 proto kernel metric 256 pref medium 2001:db8:2::/64 dev eth1 proto kernel metric 256 pref medium 2002:db8:1::/64 dev tun0 proto kernel metric 256 pref medium default via 2002:db8:1::2 dev tun0 metric 1024 pref medium $nft list ruleset table inet filter { flowtable ft { hook ingress priority filter devices = { eth0, eth1 } } chain forward { type filter hook forward priority filter; policy accept; meta l4proto { tcp, udp } flow add @ft } } Fixes: b5964aac51e0 ("netfilter: flowtable: consolidate xmit path") Signed-off-by: Lorenzo Bianconi Signed-off-by: Florian Westphal --- net/netfilter/nf_flow_table_path.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index f0984cf69a09b..eb24fe2715dcd 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -250,6 +250,9 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) nft_dev_path_info(&stack, &info, ha, &ft->data); + if (info.outdev) + route->tuple[dir].out.ifindex = info.outdev->ifindex; + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) return; @@ -269,7 +272,6 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, route->tuple[!dir].in.num_encaps = info.num_encaps; route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; - route->tuple[dir].out.ifindex = info.outdev->ifindex; if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); From b8a81b0ce539e021ac72825238aea1eb657000f0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 9 Dec 2025 00:03:36 +0100 Subject: [PATCH 043/214] selftests: netfilter: prefer xfail in case race wasn't triggered Jakub says: "We try to reserve SKIP for tests skipped because tool is missing in env, something isn't built into the kernel etc." use xfail, we can't force the race condition to appear at will so its expected that the test 'fails' occasionally. Fixes: 78a588363587 ("selftests: netfilter: add conntrack clash resolution test case") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20251206175647.5c32f419@kernel.org/ Signed-off-by: Florian Westphal --- tools/testing/selftests/net/netfilter/conntrack_clash.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/netfilter/conntrack_clash.sh b/tools/testing/selftests/net/netfilter/conntrack_clash.sh index 7fc6c5dbd5516..84b8eb12143ae 100755 --- a/tools/testing/selftests/net/netfilter/conntrack_clash.sh +++ b/tools/testing/selftests/net/netfilter/conntrack_clash.sh @@ -116,7 +116,7 @@ run_one_clash_test() # not a failure: clash resolution logic did not trigger. # With right timing, xmit completed sequentially and # no parallel insertion occurs. - return $ksft_skip + return $ksft_xfail } run_clash_test() @@ -133,12 +133,12 @@ run_clash_test() if [ $rv -eq 0 ];then echo "PASS: clash resolution test for $daddr:$dport on attempt $i" return 0 - elif [ $rv -eq $ksft_skip ]; then + elif [ $rv -eq $ksft_xfail ]; then softerr=1 fi done - [ $softerr -eq 1 ] && echo "SKIP: clash resolution for $daddr:$dport did not trigger" + [ $softerr -eq 1 ] && echo "XFAIL: clash resolution for $daddr:$dport did not trigger" } ip link add veth0 netns "$nsclient1" type veth peer name veth0 netns "$nsrouter" @@ -167,8 +167,7 @@ load_simple_ruleset "$nsclient2" run_clash_test "$nsclient2" "$nsclient2" 127.0.0.1 9001 if [ $clash_resolution_active -eq 0 ];then - [ "$ret" -eq 0 ] && ret=$ksft_skip - echo "SKIP: Clash resolution did not trigger" + [ "$ret" -eq 0 ] && ret=$ksft_xfail fi exit $ret From 0842e34849f65dc0aef0c7a0baae1dceb2b8bb33 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Tue, 9 Dec 2025 16:29:01 +0100 Subject: [PATCH 044/214] selftests: net: lib: tc_rule_stats_get(): Don't hard-code array index Flower is commonly used to match on packets in many bash-based selftests. A dump of a flower filter including statistics looks something like this: [ { "protocol": "all", "pref": 49152, "kind": "flower", "chain": 0 }, { ... "options": { ... "actions": [ { ... "stats": { "bytes": 0, "packets": 0, "drops": 0, "overlimits": 0, "requeues": 0, "backlog": 0, "qlen": 0 } } ] } } ] The JQ query in the helper function tc_rule_stats_get() assumes this form and looks for the second element of the array. However, a dump of a u32 filter looks like this: [ { "protocol": "all", "pref": 49151, "kind": "u32", "chain": 0 }, { "protocol": "all", "pref": 49151, "kind": "u32", "chain": 0, "options": { "fh": "800:", "ht_divisor": 1 } }, { ... "options": { ... "actions": [ { ... "stats": { "bytes": 0, "packets": 0, "drops": 0, "overlimits": 0, "requeues": 0, "backlog": 0, "qlen": 0 } } ] } }, ] There's an extra element which the JQ query ends up choosing. Instead of hard-coding a particular index, look for the entry on which a selector .options.actions yields anything. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/12982a44471c834511a0ee6c1e8f57e3a5307105.1765289566.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/lib.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index f448bafb3f208..0ec131b339bc4 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -280,7 +280,8 @@ tc_rule_stats_get() local selector=${1:-.packets}; shift tc -j -s filter show dev $dev $dir pref $pref \ - | jq ".[1].options.actions[].stats$selector" + | jq ".[] | select(.options.actions) | + .options.actions[].stats$selector" } tc_rule_handle_stats_get() From 0c8b9a68b344ba2aa327278688d66c31f5f04275 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Tue, 9 Dec 2025 16:29:02 +0100 Subject: [PATCH 045/214] selftests: forwarding: vxlan_bridge_1q_mc_ul: Fix flakiness This test runs an overlay traffic, forwarded over a multicast-routed VXLAN underlay. In order to determine whether packets reach their intended destination, it uses a TC match. For convenience, it uses a flower match, which however does not allow matching on the encapsulated packet. So various service traffic ends up being indistinguishable from the test packets, and ends up confusing the test. To alleviate the problem, the test uses sleep to allow the necessary service traffic to run and clear the channel, before running the test traffic. This worked for a while, but lately we have nevertheless seen flakiness of the test in the CI. Fix the issue by using u32 to match the encapsulated packet as well. The confusing packets seem to always be IPv6 multicast listener reports. Realistically they could be ARP or other ICMP6 traffic as well. Therefore look for ethertype IPv4 in the IPv4 traffic test, and for IPv6 / UDP combination in the IPv6 traffic test. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/6438cb1613a2a667d3ff64089eb5994778f247af.1765289566.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/forwarding/config | 1 + .../net/forwarding/vxlan_bridge_1q_mc_ul.sh | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/config b/tools/testing/selftests/net/forwarding/config index ce64518aaa111..75a6c3d3c1da3 100644 --- a/tools/testing/selftests/net/forwarding/config +++ b/tools/testing/selftests/net/forwarding/config @@ -29,6 +29,7 @@ CONFIG_NET_ACT_VLAN=m CONFIG_NET_CLS_BASIC=m CONFIG_NET_CLS_FLOWER=m CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_CLS_U32=m CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_META=m CONFIG_NETFILTER=y diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh index 6a570d256e07b..5ce19ca088461 100755 --- a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh +++ b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh @@ -138,13 +138,18 @@ install_capture() defer tc qdisc del dev "$dev" clsact tc filter add dev "$dev" ingress proto ip pref 104 \ - flower skip_hw ip_proto udp dst_port "$VXPORT" \ - action pass + u32 match ip protocol 0x11 0xff \ + match u16 "$VXPORT" 0xffff at 0x16 \ + match u16 0x0800 0xffff at 0x30 \ + action pass defer tc filter del dev "$dev" ingress proto ip pref 104 tc filter add dev "$dev" ingress proto ipv6 pref 106 \ - flower skip_hw ip_proto udp dst_port "$VXPORT" \ - action pass + u32 match ip6 protocol 0x11 0xff \ + match u16 "$VXPORT" 0xffff at 0x2a \ + match u16 0x86dd 0xffff at 0x44 \ + match u8 0x11 0xff at 0x4c \ + action pass defer tc filter del dev "$dev" ingress proto ipv6 pref 106 } From 514520b34ba7d0eb36890f9f9c5c874a7e41544e Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Tue, 9 Dec 2025 16:29:03 +0100 Subject: [PATCH 046/214] selftests: forwarding: vxlan_bridge_1q_mc_ul: Drop useless sleeping After fixing traffic matching in the previous patch, the test does not need to use the sleep anymore. So drop vx_wait() altogether, migrate all callers of vx{10,20}_create_wait() to the corresponding _create(), and drop the now unused _create_wait() helpers. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/eabfe4fa12ae788cf3b8c5c876a989de81dfc3d3.1765289566.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski --- .../net/forwarding/vxlan_bridge_1q_mc_ul.sh | 63 +++++++------------ 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh index 5ce19ca088461..2cf4c6d9245ba 100755 --- a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh +++ b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_mc_ul.sh @@ -253,13 +253,6 @@ vx_create() } export -f vx_create -vx_wait() -{ - # Wait for all the ARP, IGMP etc. noise to settle down so that the - # tunnel is clear for measurements. - sleep 10 -} - vx10_create() { vx_create vx10 10 id 1000 "$@" @@ -272,18 +265,6 @@ vx20_create() } export -f vx20_create -vx10_create_wait() -{ - vx10_create "$@" - vx_wait -} - -vx20_create_wait() -{ - vx20_create "$@" - vx_wait -} - ns_init_common() { local ns=$1; shift @@ -559,7 +540,7 @@ ipv4_nomcroute() # Install a misleading (S,G) rule to attempt to trick the system into # pushing the packets elsewhere. adf_install_broken_sg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$swp2" + vx10_create local 192.0.2.100 group "$GROUP4" dev "$swp2" do_test 4 10 0 "IPv4 nomcroute" } @@ -567,7 +548,7 @@ ipv6_nomcroute() { # Like for IPv4, install a misleading (S,G). adf_install_broken_sg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$swp2" + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$swp2" do_test 6 10 0 "IPv6 nomcroute" } @@ -586,35 +567,35 @@ ipv6_nomcroute_rx() ipv4_mcroute() { adf_install_sg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute do_test 4 10 10 "IPv4 mcroute" } ipv6_mcroute() { adf_install_sg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute do_test 6 10 10 "IPv6 mcroute" } ipv4_mcroute_rx() { adf_install_sg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute ipv4_do_test_rx 0 "IPv4 mcroute ping" } ipv6_mcroute_rx() { adf_install_sg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute ipv6_do_test_rx 0 "IPv6 mcroute ping" } ipv4_mcroute_changelink() { adf_install_sg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" ip link set dev vx10 type vxlan mcroute sleep 1 do_test 4 10 10 "IPv4 mcroute changelink" @@ -623,7 +604,7 @@ ipv4_mcroute_changelink() ipv6_mcroute_changelink() { adf_install_sg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute ip link set dev vx20 type vxlan mcroute sleep 1 do_test 6 10 10 "IPv6 mcroute changelink" @@ -632,47 +613,47 @@ ipv6_mcroute_changelink() ipv4_mcroute_starg() { adf_install_starg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute do_test 4 10 10 "IPv4 mcroute (*,G)" } ipv6_mcroute_starg() { adf_install_starg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute do_test 6 10 10 "IPv6 mcroute (*,G)" } ipv4_mcroute_starg_rx() { adf_install_starg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute ipv4_do_test_rx 0 "IPv4 mcroute (*,G) ping" } ipv6_mcroute_starg_rx() { adf_install_starg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute ipv6_do_test_rx 0 "IPv6 mcroute (*,G) ping" } ipv4_mcroute_noroute() { - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute do_test 4 0 0 "IPv4 mcroute, no route" } ipv6_mcroute_noroute() { - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute do_test 6 0 0 "IPv6 mcroute, no route" } ipv4_mcroute_fdb() { adf_install_sg - vx10_create_wait local 192.0.2.100 dev "$IPMR" mcroute + vx10_create local 192.0.2.100 dev "$IPMR" mcroute bridge fdb add dev vx10 \ 00:00:00:00:00:00 self static dst "$GROUP4" via "$IPMR" do_test 4 10 10 "IPv4 mcroute FDB" @@ -681,7 +662,7 @@ ipv4_mcroute_fdb() ipv6_mcroute_fdb() { adf_install_sg - vx20_create_wait local 2001:db8:4::1 dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 dev "$IPMR" mcroute bridge -6 fdb add dev vx20 \ 00:00:00:00:00:00 self static dst "$GROUP6" via "$IPMR" do_test 6 10 10 "IPv6 mcroute FDB" @@ -691,7 +672,7 @@ ipv6_mcroute_fdb() ipv4_mcroute_fdb_oif0() { adf_install_sg - vx10_create_wait local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.100 group "$GROUP4" dev "$IPMR" mcroute bridge fdb del dev vx10 00:00:00:00:00:00 bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" do_test 4 10 10 "IPv4 mcroute oif=0" @@ -708,7 +689,7 @@ ipv6_mcroute_fdb_oif0() defer ip -6 route del table local multicast "$GROUP6/128" dev "$IPMR" adf_install_sg - vx20_create_wait local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:4::1 group "$GROUP6" dev "$IPMR" mcroute bridge -6 fdb del dev vx20 00:00:00:00:00:00 bridge -6 fdb add dev vx20 00:00:00:00:00:00 self static dst "$GROUP6" do_test 6 10 10 "IPv6 mcroute oif=0" @@ -721,7 +702,7 @@ ipv4_mcroute_fdb_oif0_sep() adf_install_sg_sep adf_ip_addr_add lo 192.0.2.120/28 - vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute bridge fdb del dev vx10 00:00:00:00:00:00 bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" do_test 4 10 10 "IPv4 mcroute TX!=RX oif=0" @@ -732,7 +713,7 @@ ipv4_mcroute_fdb_oif0_sep_rx() adf_install_sg_sep_rx lo adf_ip_addr_add lo 192.0.2.120/28 - vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute bridge fdb del dev vx10 00:00:00:00:00:00 bridge fdb add dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" ipv4_do_test_rx 0 "IPv4 mcroute TX!=RX oif=0 ping" @@ -743,7 +724,7 @@ ipv4_mcroute_fdb_sep_rx() adf_install_sg_sep_rx lo adf_ip_addr_add lo 192.0.2.120/28 - vx10_create_wait local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute + vx10_create local 192.0.2.120 group "$GROUP4" dev "$IPMR" mcroute bridge fdb del dev vx10 00:00:00:00:00:00 bridge fdb add \ dev vx10 00:00:00:00:00:00 self static dst "$GROUP4" via lo @@ -755,7 +736,7 @@ ipv6_mcroute_fdb_sep_rx() adf_install_sg_sep_rx "X$IPMR" adf_ip_addr_add "X$IPMR" 2001:db8:5::1/64 - vx20_create_wait local 2001:db8:5::1 group "$GROUP6" dev "$IPMR" mcroute + vx20_create local 2001:db8:5::1 group "$GROUP6" dev "$IPMR" mcroute bridge -6 fdb del dev vx20 00:00:00:00:00:00 bridge -6 fdb add dev vx20 00:00:00:00:00:00 \ self static dst "$GROUP6" via "X$IPMR" From 71cfa7c893a05d09e7dc14713b27a8309fd4a2db Mon Sep 17 00:00:00 2001 From: Marcus Hughes Date: Sun, 7 Dec 2025 21:03:55 +0000 Subject: [PATCH 047/214] net: sfp: extend Potron XGSPON quirk to cover additional EEPROM variant Some Potron SFP+ XGSPON ONU sticks are shipped with different EEPROM vendor ID and vendor name strings, but are otherwise functionally identical to the existing "Potron SFP+ XGSPON ONU Stick" handled by sfp_quirk_potron(). These modules, including units distributed under the "Better Internet" branding, use the same UART pin assignment and require the same TX_FAULT/LOS behaviour and boot delay. Re-use the existing Potron quirk for this EEPROM variant. Signed-off-by: Marcus Hughes Link: https://patch.msgid.link/20251207210355.333451-1-marcus.hughes@betterinternet.ltd Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 0401fa6b24d25..6166e91963644 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -497,6 +497,8 @@ static const struct sfp_quirk sfp_quirks[] = { SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex, sfp_fixup_nokia), + SFP_QUIRK_F("BIDB", "X-ONU-SFPP", sfp_fixup_potron), + // FLYPRO SFP-10GT-CS-30M uses Rollball protocol to talk to the PHY. SFP_QUIRK_F("FLYPRO", "SFP-10GT-CS-30M", sfp_fixup_rollball), From 8a11ff0948b5ad09b71896b7ccc850625f9878d1 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 4 Dec 2025 21:30:47 +0800 Subject: [PATCH 048/214] caif: fix integer underflow in cffrml_receive() The cffrml_receive() function extracts a length field from the packet header and, when FCS is disabled, subtracts 2 from this length without validating that len >= 2. If an attacker sends a malicious packet with a length field of 0 or 1 to an interface with FCS disabled, the subtraction causes an integer underflow. This can lead to memory exhaustion and kernel instability, potential information disclosure if padding contains uninitialized kernel memory. Fix this by validating that len >= 2 before performing the subtraction. Reported-by: Yuhao Jiang Reported-by: Junrui Luo Fixes: b482cd2053e3 ("net-caif: add CAIF core protocol stack") Signed-off-by: Junrui Luo Reviewed-by: Simon Horman Link: https://patch.msgid.link/SYBPR01MB7881511122BAFEA8212A1608AFA6A@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Jakub Kicinski --- net/caif/cffrml.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c index 6651a8dc62e04..d4d63586053ad 100644 --- a/net/caif/cffrml.c +++ b/net/caif/cffrml.c @@ -92,8 +92,15 @@ static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt) len = le16_to_cpu(tmp); /* Subtract for FCS on length if FCS is not used. */ - if (!this->dofcs) + if (!this->dofcs) { + if (len < 2) { + ++cffrml_rcv_error; + pr_err("Invalid frame length (%d)\n", len); + cfpkt_destroy(pkt); + return -EPROTO; + } len -= 2; + } if (cfpkt_setlen(pkt, len) < 0) { ++cffrml_rcv_error; From b1e125ae425aba9b45252e933ca8df52a843ec70 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Mon, 8 Dec 2025 16:01:24 -0300 Subject: [PATCH 049/214] net/sched: ets: Remove drr class from the active list if it changes to strict Whenever a user issues an ets qdisc change command, transforming a drr class into a strict one, the ets code isn't checking whether that class was in the active list and removing it. This means that, if a user changes a strict class (which was in the active list) back to a drr one, that class will be added twice to the active list [1]. Doing so with the following commands: tc qdisc add dev lo root handle 1: ets bands 2 strict 1 tc qdisc add dev lo parent 1:2 handle 20: \ tbf rate 8bit burst 100b latency 1s tc filter add dev lo parent 1: basic classid 1:2 ping -c1 -W0.01 -s 56 127.0.0.1 tc qdisc change dev lo root handle 1: ets bands 2 strict 2 tc qdisc change dev lo root handle 1: ets bands 2 strict 1 ping -c1 -W0.01 -s 56 127.0.0.1 Will trigger the following splat with list debug turned on: [ 59.279014][ T365] ------------[ cut here ]------------ [ 59.279452][ T365] list_add double add: new=ffff88801d60e350, prev=ffff88801d60e350, next=ffff88801d60e2c0. [ 59.280153][ T365] WARNING: CPU: 3 PID: 365 at lib/list_debug.c:35 __list_add_valid_or_report+0x17f/0x220 [ 59.280860][ T365] Modules linked in: [ 59.281165][ T365] CPU: 3 UID: 0 PID: 365 Comm: tc Not tainted 6.18.0-rc7-00105-g7e9f13163c13-dirty #239 PREEMPT(voluntary) [ 59.281977][ T365] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 59.282391][ T365] RIP: 0010:__list_add_valid_or_report+0x17f/0x220 [ 59.282842][ T365] Code: 89 c6 e8 d4 b7 0d ff 90 0f 0b 90 90 31 c0 e9 31 ff ff ff 90 48 c7 c7 e0 a0 22 9f 48 89 f2 48 89 c1 4c 89 c6 e8 b2 b7 0d ff 90 <0f> 0b 90 90 31 c0 e9 0f ff ff ff 48 89 f7 48 89 44 24 10 4c 89 44 ... [ 59.288812][ T365] Call Trace: [ 59.289056][ T365] [ 59.289224][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.289546][ T365] ets_qdisc_change+0xd2b/0x1e80 [ 59.289891][ T365] ? __lock_acquire+0x7e7/0x1be0 [ 59.290223][ T365] ? __pfx_ets_qdisc_change+0x10/0x10 [ 59.290546][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.290898][ T365] ? __mutex_trylock_common+0xda/0x240 [ 59.291228][ T365] ? __pfx___mutex_trylock_common+0x10/0x10 [ 59.291655][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.291993][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.292313][ T365] ? trace_contention_end+0xc8/0x110 [ 59.292656][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.293022][ T365] ? srso_alias_return_thunk+0x5/0xfbef5 [ 59.293351][ T365] tc_modify_qdisc+0x63a/0x1cf0 Fix this by always checking and removing an ets class from the active list when changing it to strict. [1] https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/tree/net/sched/sch_ets.c?id=ce052b9402e461a9aded599f5b47e76bc727f7de#n663 Fixes: cd9b50adc6bb9 ("net/sched: ets: fix crash when flipping from 'strict' to 'quantum'") Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Reviewed-by: Petr Machata Link: https://patch.msgid.link/20251208190125.1868423-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/sch_ets.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index ae46643e596d3..306e046276d46 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -664,6 +664,10 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, q->classes[i].deficit = quanta[i]; } } + for (i = q->nstrict; i < nstrict; i++) { + if (cl_is_active(&q->classes[i])) + list_del_init(&q->classes[i].alist); + } WRITE_ONCE(q->nstrict, nstrict); memcpy(q->prio2band, priomap, sizeof(priomap)); From 5914428e0e44c4dcb64ad42cc37fa23a57fd1c5c Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Mon, 8 Dec 2025 16:01:25 -0300 Subject: [PATCH 050/214] selftests/tc-testing: Create tests to exercise ets classes active list misplacements Add a test case for a bug fixed by Jamal [1] and for scenario where an ets drr class is inserted into the active list twice. - Try to delete ets drr class' qdisc while still keeping it in the active list - Try to add ets class to the active list twice [1] https://lore.kernel.org/netdev/20251128151919.576920-1-jhs@mojatatu.com/ Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Reviewed-by: Petr Machata Link: https://patch.msgid.link/20251208190125.1868423-2-victor@mojatatu.com Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/infra/qdiscs.json | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index 47de27fd4f90f..6a39640aa2a86 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -1033,5 +1033,83 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "6e4f", + "name": "Try to delete ets drr class' qdisc while still keeping it in the active list", + "category": [ + "qdisc", + "ets", + "tbf" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: ets bands 2 strict 1", + "$TC qdisc add dev $DUMMY parent 1:2 handle 20: tbf rate 8bit burst 100b latency 1s", + "$TC filter add dev $DUMMY parent 1: basic classid 1:2", + "ping -c2 -W0.01 -s 56 -I $DUMMY 10.10.11.11 || true", + "$TC qdisc change dev $DUMMY root handle 1: ets bands 2 strict 2", + "$TC qdisc change dev $DUMMY root handle 1: ets bands 1 strict 1" + ], + "cmdUnderTest": "ping -c1 -W0.01 -s 56 -I $DUMMY 10.10.11.11", + "expExitCode": "1", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY root", + "matchJSON": [ + { + "kind": "ets", + "handle": "1:", + "bytes": 196, + "packets": 2 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root handle 1:" + ] + }, + { + "id": "0b8f", + "name": "Try to add ets class to the active list twice", + "category": [ + "qdisc", + "ets", + "tbf" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: ets bands 2 strict 1", + "$TC qdisc add dev $DUMMY parent 1:2 handle 20: tbf rate 8bit burst 100b latency 1s", + "$TC filter add dev $DUMMY parent 1: basic classid 1:2", + "ping -c2 -W0.01 -s 56 -I $DUMMY 10.10.11.11 || true", + "$TC qdisc change dev $DUMMY root handle 1: ets bands 2 strict 2", + "$TC qdisc change dev $DUMMY root handle 1: ets bands 2 strict 1" + ], + "cmdUnderTest": "ping -c1 -W0.01 -s 56 -I $DUMMY 10.10.11.11", + "expExitCode": "1", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY root", + "matchJSON": [ + { + "kind": "ets", + "handle": "1:", + "bytes": 98, + "packets": 1 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root handle 1:" + ] } ] From 885bebac9909994050bbbeed0829c727e42bd1b7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 9 Dec 2025 09:56:39 +0300 Subject: [PATCH 051/214] nfc: pn533: Fix error code in pn533_acr122_poweron_rdr() Set the error code if "transferred != sizeof(cmd)" instead of returning success. Fixes: dbafc28955fa ("NFC: pn533: don't send USB data off of the stack") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/aTfIJ9tZPmeUF4W1@stanley.mountain Signed-off-by: Jakub Kicinski --- drivers/nfc/pn533/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index ffd7367ce1194..018a80674f06e 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -406,7 +406,7 @@ static int pn533_acr122_poweron_rdr(struct pn533_usb_phy *phy) if (rc || (transferred != sizeof(cmd))) { nfc_err(&phy->udev->dev, "Reader power on cmd error %d\n", rc); - return rc; + return rc ?: -EINVAL; } rc = usb_submit_urb(phy->in_urb, GFP_KERNEL); From 99c6931fe1f5d3de1174ce771cb86c57f75bff14 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 2 Dec 2025 15:20:15 +0100 Subject: [PATCH 052/214] MAINTAINERS: Remove Jozsef Kadlecsik from MAINTAINERS file I'm retiring from maintaining netfilter. I'll still keep an eye on ipset and respond to anything related to it. Thank you! Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- CREDITS | 1 + MAINTAINERS | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index fa5397f4ebcdd..cb9b2d6184d01 100644 --- a/CREDITS +++ b/CREDITS @@ -1983,6 +1983,7 @@ D: netfilter: TCP window tracking code D: netfilter: raw table D: netfilter: iprange match D: netfilter: new logging interfaces +D: netfilter: ipset D: netfilter: various other hacks S: Tata S: Hungary diff --git a/MAINTAINERS b/MAINTAINERS index e36689cd7cc7b..45b22f4205936 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17808,7 +17808,6 @@ F: drivers/net/ethernet/neterion/ NETFILTER M: Pablo Neira Ayuso -M: Jozsef Kadlecsik M: Florian Westphal R: Phil Sutter L: netfilter-devel@vger.kernel.org From 5ec8ca26fe93103577c904644b0957f069d0051a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 8 Dec 2025 16:00:34 +0100 Subject: [PATCH 053/214] netfilter: nf_nat: remove bogus direction check Jakub reports spurious failures of the 'conntrack_reverse_clash.sh' selftest. A bogus test makes nat core resort to port rewrite even though there is no need for this. When the test is made, nf_nat_used_tuple() would already have caused us to return if no other CPU had added a colliding entry. Moreover, nf_nat_used_tuple() would have ignored the colliding entry if their origin tuples had been the same. All that is left to check is if the colliding entry in the hash table is subject to NAT, and, if its not, if our entry matches in the reverse direction, e.g. hash table has addr1:1234 -> addr2:80, and we want to commit addr2:80 -> addr1:1234. Because we already checked that neither the new nor the committed entry is subject to NAT we only have to check origin vs. reply tuple: for non-nat entries, the reply tuple is always the inverted original. Just in case there are more problems extend the error reporting in the selftest while at it and dump conntrack table/stats on error. Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20251206175135.4a56591b@kernel.org/ Fixes: d8f84a9bc7c4 ("netfilter: nf_nat: don't try nat source port reallocation for reverse dir clash") Signed-off-by: Florian Westphal --- net/netfilter/nf_nat_core.c | 14 +------------- .../net/netfilter/conntrack_reverse_clash.c | 13 +++++++++---- .../net/netfilter/conntrack_reverse_clash.sh | 2 ++ 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 78a61dac4ade8..e6b24586d2fed 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -294,25 +294,13 @@ nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple, ct = nf_ct_tuplehash_to_ctrack(thash); - /* NB: IP_CT_DIR_ORIGINAL should be impossible because - * nf_nat_used_tuple() handles origin collisions. - * - * Handle remote chance other CPU confirmed its ct right after. - */ - if (thash->tuple.dst.dir != IP_CT_DIR_REPLY) - goto out; - /* clashing connection subject to NAT? Retry with new tuple. */ if (READ_ONCE(ct->status) & uses_nat) goto out; if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - &ignored_ct->tuplehash[IP_CT_DIR_REPLY].tuple) && - nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, - &ignored_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)) { + &ignored_ct->tuplehash[IP_CT_DIR_REPLY].tuple)) taken = false; - goto out; - } out: nf_ct_put(ct); return taken; diff --git a/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c b/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c index 507930cee8cb6..462d628cc3bdb 100644 --- a/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c +++ b/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c @@ -33,9 +33,14 @@ static void die(const char *e) exit(111); } -static void die_port(uint16_t got, uint16_t want) +static void die_port(const struct sockaddr_in *sin, uint16_t want) { - fprintf(stderr, "Port number changed, wanted %d got %d\n", want, ntohs(got)); + uint16_t got = ntohs(sin->sin_port); + char str[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)); + + fprintf(stderr, "Port number changed, wanted %d got %d from %s\n", want, got, str); exit(1); } @@ -100,7 +105,7 @@ int main(int argc, char *argv[]) die("child recvfrom"); if (peer.sin_port != htons(PORT)) - die_port(peer.sin_port, PORT); + die_port(&peer, PORT); } else { if (sendto(s2, buf, LEN, 0, (struct sockaddr *)&sa1, sizeof(sa1)) != LEN) continue; @@ -109,7 +114,7 @@ int main(int argc, char *argv[]) die("parent recvfrom"); if (peer.sin_port != htons((PORT + 1))) - die_port(peer.sin_port, PORT + 1); + die_port(&peer, PORT + 1); } } diff --git a/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh b/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh index a24c896347a88..dc7e9d6da0624 100755 --- a/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh +++ b/tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh @@ -45,6 +45,8 @@ if ip netns exec "$ns0" ./conntrack_reverse_clash; then echo "PASS: No SNAT performed for null bindings" else echo "ERROR: SNAT performed without any matching snat rule" + ip netns exec "$ns0" conntrack -L + ip netns exec "$ns0" conntrack -S exit 1 fi From a67fd55f6a09f4119b7232c19e0f348fe31ab0db Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 19 Nov 2025 13:42:05 +0100 Subject: [PATCH 054/214] netfilter: nf_tables: remove redundant chain validation on register store This validation predates the introduction of the state machine that determines when to enter slow path validation for error reporting. Currently, table validation is perform when: - new rule contains expressions that need validation. - new set element with jump/goto verdict. Validation on register store skips most checks with no basechains, still this walks the graph searching for loops and ensuring expressions are called from the right hook. Remove this. Fixes: a654de8fdc18 ("netfilter: nf_tables: fix chain dependency validation") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_tables_api.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index f3de2f9bbebf1..c46b1bb0efe0f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -11676,21 +11676,10 @@ static int nft_validate_register_store(const struct nft_ctx *ctx, enum nft_data_types type, unsigned int len) { - int err; - switch (reg) { case NFT_REG_VERDICT: if (type != NFT_DATA_VERDICT) return -EINVAL; - - if (data != NULL && - (data->verdict.code == NFT_GOTO || - data->verdict.code == NFT_JUMP)) { - err = nft_chain_validate(ctx, data->verdict.chain); - if (err < 0) - return err; - } - break; default: if (type != NFT_DATA_VALUE) From f3ccdfda345ca9a624ea425840a926b8338c1e25 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 25 Nov 2025 09:38:49 +0800 Subject: [PATCH 055/214] wifi: rtw88: limit indirect IO under powered off for RTL8822CS The indirect IO is necessary for RTL8822CS, but not necessary for other chips. Otherwiese, it throws errors and becomes unusable. rtw88_8723cs mmc1:0001:1: WOW Firmware version 11.0.0, H2C version 0 rtw88_8723cs mmc1:0001:1: Firmware version 11.0.0, H2C version 0 rtw88_8723cs mmc1:0001:1: sdio read32 failed (0xf0): -110 rtw88_8723cs mmc1:0001:1: sdio write8 failed (0x1c): -110 rtw88_8723cs mmc1:0001:1: sdio read32 failed (0xf0): -110 By vendor driver, only RTL8822CS and RTL8822ES need indirect IO, but RTL8822ES isn't supported yet. Therefore, limit it to RTL8822CS only. Reported-by: Andrey Skvortsov Closes: https://lore.kernel.org/linux-wireless/07a32e2d6c764eb1bd9415b5a921a652@realtek.com/T/#m997b4522f7209ba629561c776bfd1d13ab24c1d4 Fixes: 58de1f91e033 ("wifi: rtw88: sdio: use indirect IO for device registers before power-on") Signed-off-by: Ping-Ke Shih Tested-by: Andrey Skvortsov Link: https://patch.msgid.link/1764034729-1251-1-git-send-email-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/sdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index 99d7c629eac6f..e35de52d8eb43 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -144,8 +144,10 @@ static u32 rtw_sdio_to_io_address(struct rtw_dev *rtwdev, u32 addr, static bool rtw_sdio_use_direct_io(struct rtw_dev *rtwdev, u32 addr) { + bool might_indirect_under_power_off = rtwdev->chip->id == RTW_CHIP_TYPE_8822C; + if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags) && - !rtw_sdio_is_bus_addr(addr)) + !rtw_sdio_is_bus_addr(addr) && might_indirect_under_power_off) return false; return !rtw_sdio_is_sdio30_supported(rtwdev) || From dd39edb445f07400e748da967a07d5dca5c5f96e Mon Sep 17 00:00:00 2001 From: Morning Star Date: Thu, 27 Nov 2025 16:37:08 +0800 Subject: [PATCH 056/214] wifi: rtlwifi: 8192cu: fix tid out of range in rtl92cu_tx_fill_desc() TID getting from ieee80211_get_tid() might be out of range of array size of sta_entry->tids[], so check TID is less than MAX_TID_COUNT. Othwerwise, UBSAN warn: UBSAN: array-index-out-of-bounds in drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c:514:30 index 10 is out of range for type 'rtl_tid_data [9]' Fixes: 8ca4cdef9329 ("wifi: rtlwifi: rtl8192cu: Fix TX aggregation") Signed-off-by: Morning Star Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1764232628-13625-1-git-send-email-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index aa702ba7c9f54..d6c35e8d02a58 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -511,7 +511,8 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, if (sta) { sta_entry = (struct rtl_sta_info *)sta->drv_priv; tid = ieee80211_get_tid(hdr); - agg_state = sta_entry->tids[tid].agg.agg_state; + if (tid < MAX_TID_COUNT) + agg_state = sta_entry->tids[tid].agg.agg_state; ampdu_density = sta->deflink.ht_cap.ampdu_density; } From 0ff5e81e1518868286d7a1cda192c23db3110b7c Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 6 Dec 2025 20:32:43 +0200 Subject: [PATCH 057/214] Revert "wifi: rtw88: add WQ_UNBOUND to alloc_workqueue users" This reverts commit 9c194fe4625db18f93d5abcfb7f7997557a0b29d. This commit breaks all USB wifi adapters supported by rtw88: usb 1-2: new high-speed USB device number 6 using xhci_hcd usb 1-2: New USB device found, idVendor=2357, idProduct=0138, bcdDevice= 2.10 usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-2: Product: 802.11ac NIC usb 1-2: Manufacturer: Realtek usb 1-2: SerialNumber: 123456 ------------[ cut here ]------------ WARNING: CPU: 3 PID: 152 at kernel/workqueue.c:5667 alloc_workqueue_noprof+0x676/0x770 [...] Call Trace: ? rtw_usb_probe+0x30e/0xa5c [rtw88_usb 4af3cb64eedafeecbfb08f80c1e9e2893e2ee7a6] rtw_usb_probe+0x3eb/0xa5c [rtw88_usb 4af3cb64eedafeecbfb08f80c1e9e2893e2ee7a6] usb_probe_interface+0xdd/0x2c0 really_probe+0xdb/0x340 ? pm_runtime_barrier+0x55/0x90 ? __pfx___device_attach_driver+0x10/0x10 __driver_probe_device+0x78/0x140 driver_probe_device+0x1f/0xa0 __device_attach_driver+0x89/0x110 bus_for_each_drv+0x8f/0xe0 __device_attach+0xb0/0x1c0 bus_probe_device+0x90/0xa0 device_add+0x663/0x880 usb_set_configuration+0x5a5/0x870 usb_generic_driver_probe+0x4a/0x70 usb_probe_device+0x3d/0x140 ? driver_sysfs_add+0x59/0xd0 really_probe+0xdb/0x340 ? pm_runtime_barrier+0x55/0x90 ? __pfx___device_attach_driver+0x10/0x10 __driver_probe_device+0x78/0x140 driver_probe_device+0x1f/0xa0 __device_attach_driver+0x89/0x110 bus_for_each_drv+0x8f/0xe0 __device_attach+0xb0/0x1c0 bus_probe_device+0x90/0xa0 device_add+0x663/0x880 usb_new_device.cold+0x141/0x3b5 hub_event+0x1132/0x1900 ? page_counter_uncharge+0x4a/0x90 process_one_work+0x190/0x350 worker_thread+0x2d7/0x410 ? __pfx_worker_thread+0x10/0x10 kthread+0xf9/0x240 ? __pfx_kthread+0x10/0x10 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x1c1/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 ---[ end trace 0000000000000000 ]--- rtw88_8822bu 1-2:1.0: failed to create RX work queue rtw88_8822bu 1-2:1.0: failed to init USB RX rtw88_8822bu 1-2:1.0: Firmware version 27.2.0, H2C version 13 rtw88_8822bu 1-2:1.0: probe with driver rtw88_8822bu failed with error -12 WQ_UNBOUND is not compatible with WQ_BH. Comment in enum wq_flags in workqueue.h says: /* BH wq only allows the following flags */ __WQ_BH_ALLOWS = WQ_BH | WQ_HIGHPRI | WQ_PERCPU, Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d57efe48-b8ff-4bf1-942c-7e808535eda6@gmail.com --- drivers/net/wireless/realtek/rtw88/usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 009202c627d25..3b5126ffc81a1 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -965,8 +965,7 @@ static int rtw_usb_init_rx(struct rtw_dev *rtwdev) struct sk_buff *rx_skb; int i; - rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH | WQ_UNBOUND, - 0); + rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH, 0); if (!rtwusb->rxwq) { rtw_err(rtwdev, "failed to create RX work queue\n"); return -ENOMEM; From 635bc4def026a24e071436f4f356ea08c0eed6ff Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 7 Dec 2025 11:44:55 +0100 Subject: [PATCH 058/214] fsnotify: do not generate ACCESS/MODIFY events on child for special files inotify/fanotify do not allow users with no read access to a file to subscribe to events (e.g. IN_ACCESS/IN_MODIFY), but they do allow the same user to subscribe for watching events on children when the user has access to the parent directory (e.g. /dev). Users with no read access to a file but with read access to its parent directory can still stat the file and see if it was accessed/modified via atime/mtime change. The same is not true for special files (e.g. /dev/null). Users will not generally observe atime/mtime changes when other users read/write to special files, only when someone sets atime/mtime via utimensat(). Align fsnotify events with this stat behavior and do not generate ACCESS/MODIFY events to parent watchers on read/write of special files. The events are still generated to parent watchers on utimensat(). This closes some side-channels that could be possibly used for information exfiltration [1]. [1] https://snee.la/pdf/pubs/file-notification-attacks.pdf Reported-by: Sudheendra Raghav Neela CC: stable@vger.kernel.org Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/fsnotify.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 46bfc543f9467..63dd44931989d 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -270,8 +270,15 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, /* * Include parent/name in notification either if some notification * groups require parent info or the parent is interested in this event. + * The parent interest in ACCESS/MODIFY events does not apply to special + * files, where read/write are not on the filesystem of the parent and + * events can provide an undesirable side-channel for information + * exfiltration. */ - parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS; + parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS && + !(data_type == FSNOTIFY_EVENT_PATH && + d_is_special(dentry) && + (mask & (FS_ACCESS | FS_MODIFY))); if (parent_needed || parent_interested) { /* When notifying parent, child should be passed as data */ WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type)); From 6f7c877cc397ba3c6d8ba44d4a604df3d4182eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahelenia=20Ziemia=C5=84ska?= Date: Mon, 8 Dec 2025 23:20:24 +0100 Subject: [PATCH 059/214] fs: send fsnotify_xattr()/IN_ATTRIB from vfs_fileattr_set()/chattr(1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently it seems impossible to observe these changes to the file's attributes. It's useful to be able to do this to see when the file becomes immutable, for example, so emit IN_ATTRIB via fsnotify_xattr(), like when changing other inode attributes. Signed-off-by: Ahelenia Ziemiańska Link: https://patch.msgid.link/iyvn6qjotpu6cei5jdtsoibfcp6l6rgvn47cwgaucgtucpfy2s@tarta.nabijaczleweli.xyz Signed-off-by: Jan Kara --- fs/file_attr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/file_attr.c b/fs/file_attr.c index 1dcec88c06805..fac41048f7bc8 100644 --- a/fs/file_attr.c +++ b/fs/file_attr.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -298,6 +299,7 @@ int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, err = inode->i_op->fileattr_set(idmap, dentry, fa); if (err) goto out; + fsnotify_xattr(dentry); } out: From 8e1a1bc4f5a42747c08130b8242ebebd1210b32f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 7 Jul 2024 01:18:25 +0200 Subject: [PATCH 060/214] netfilter: nf_tables: avoid chain re-validation if possible Hamza Mahfooz reports cpu soft lock-ups in nft_chain_validate(): watchdog: BUG: soft lockup - CPU#1 stuck for 27s! [iptables-nft-re:37547] [..] RIP: 0010:nft_chain_validate+0xcb/0x110 [nf_tables] [..] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_immediate_validate+0x36/0x50 [nf_tables] nft_chain_validate+0xc9/0x110 [nf_tables] nft_table_validate+0x6b/0xb0 [nf_tables] nf_tables_validate+0x8b/0xa0 [nf_tables] nf_tables_commit+0x1df/0x1eb0 [nf_tables] [..] Currently nf_tables will traverse the entire table (chain graph), starting from the entry points (base chains), exploring all possible paths (chain jumps). But there are cases where we could avoid revalidation. Consider: 1 input -> j2 -> j3 2 input -> j2 -> j3 3 input -> j1 -> j2 -> j3 Then the second rule does not need to revalidate j2, and, by extension j3, because this was already checked during validation of the first rule. We need to validate it only for rule 3. This is needed because chain loop detection also ensures we do not exceed the jump stack: Just because we know that j2 is cycle free, its last jump might now exceed the allowed stack size. We also need to update all reachable chains with the new largest observed call depth. Care has to be taken to revalidate even if the chain depth won't be an issue: chain validation also ensures that expressions are not called from invalid base chains. For example, the masquerade expression can only be called from NAT postrouting base chains. Therefore we also need to keep record of the base chain context (type, hooknum) and revalidate if the chain becomes reachable from a different hook location. Reported-by: Hamza Mahfooz Closes: https://lore.kernel.org/netfilter-devel/20251118221735.GA5477@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net/ Tested-by: Hamza Mahfooz Signed-off-by: Florian Westphal --- include/net/netfilter/nf_tables.h | 34 +++++++++++---- net/netfilter/nf_tables_api.c | 69 +++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index fab7dc73f738c..0e266c2d0e7f0 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1091,6 +1091,29 @@ struct nft_rule_blob { __attribute__((aligned(__alignof__(struct nft_rule_dp)))); }; +enum nft_chain_types { + NFT_CHAIN_T_DEFAULT = 0, + NFT_CHAIN_T_ROUTE, + NFT_CHAIN_T_NAT, + NFT_CHAIN_T_MAX +}; + +/** + * struct nft_chain_validate_state - validation state + * + * If a chain is encountered again during table validation it is + * possible to avoid revalidation provided the calling context is + * compatible. This structure stores relevant calling context of + * previous validations. + * + * @hook_mask: the hook numbers and locations the chain is linked to + * @depth: the deepest call chain level the chain is linked to + */ +struct nft_chain_validate_state { + u8 hook_mask[NFT_CHAIN_T_MAX]; + u8 depth; +}; + /** * struct nft_chain - nf_tables chain * @@ -1109,6 +1132,7 @@ struct nft_rule_blob { * @udlen: user data length * @udata: user data in the chain * @blob_next: rule blob pointer to the next in the chain + * @vstate: validation state */ struct nft_chain { struct nft_rule_blob __rcu *blob_gen_0; @@ -1128,9 +1152,10 @@ struct nft_chain { /* Only used during control plane commit phase: */ struct nft_rule_blob *blob_next; + struct nft_chain_validate_state vstate; }; -int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain); +int nft_chain_validate(const struct nft_ctx *ctx, struct nft_chain *chain); int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, const struct nft_set_iter *iter, struct nft_elem_priv *elem_priv); @@ -1138,13 +1163,6 @@ int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set); int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); -enum nft_chain_types { - NFT_CHAIN_T_DEFAULT = 0, - NFT_CHAIN_T_ROUTE, - NFT_CHAIN_T_NAT, - NFT_CHAIN_T_MAX -}; - /** * struct nft_chain_type - nf_tables chain type info * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c46b1bb0efe0f..a9f6babcc781b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -123,6 +123,29 @@ static void nft_validate_state_update(struct nft_table *table, u8 new_validate_s table->validate_state = new_validate_state; } + +static bool nft_chain_vstate_valid(const struct nft_ctx *ctx, + const struct nft_chain *chain) +{ + const struct nft_base_chain *base_chain; + enum nft_chain_types type; + u8 hooknum; + + if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain))) + return false; + + base_chain = nft_base_chain(ctx->chain); + hooknum = base_chain->ops.hooknum; + type = base_chain->type->type; + + /* chain is already validated for this call depth */ + if (chain->vstate.depth >= ctx->level && + chain->vstate.hook_mask[type] & BIT(hooknum)) + return true; + + return false; +} + static void nf_tables_trans_destroy_work(struct work_struct *w); static void nft_trans_gc_work(struct work_struct *work); @@ -4079,6 +4102,29 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *r nf_tables_rule_destroy(ctx, rule); } +static void nft_chain_vstate_update(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + const struct nft_base_chain *base_chain; + enum nft_chain_types type; + u8 hooknum; + + /* ctx->chain must hold the calling base chain. */ + if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain))) { + memset(&chain->vstate, 0, sizeof(chain->vstate)); + return; + } + + base_chain = nft_base_chain(ctx->chain); + hooknum = base_chain->ops.hooknum; + type = base_chain->type->type; + + BUILD_BUG_ON(BIT(NF_INET_NUMHOOKS) > U8_MAX); + + chain->vstate.hook_mask[type] |= BIT(hooknum); + if (chain->vstate.depth < ctx->level) + chain->vstate.depth = ctx->level; +} + /** nft_chain_validate - loop detection and hook validation * * @ctx: context containing call depth and base chain @@ -4088,15 +4134,25 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *r * and set lookups until either the jump limit is hit or all reachable * chains have been validated. */ -int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain) +int nft_chain_validate(const struct nft_ctx *ctx, struct nft_chain *chain) { struct nft_expr *expr, *last; struct nft_rule *rule; int err; + BUILD_BUG_ON(NFT_JUMP_STACK_SIZE > 255); if (ctx->level == NFT_JUMP_STACK_SIZE) return -EMLINK; + if (ctx->level > 0) { + /* jumps to base chains are not allowed. */ + if (nft_is_base_chain(chain)) + return -ELOOP; + + if (nft_chain_vstate_valid(ctx, chain)) + return 0; + } + list_for_each_entry(rule, &chain->rules, list) { if (fatal_signal_pending(current)) return -EINTR; @@ -4117,6 +4173,7 @@ int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain) } } + nft_chain_vstate_update(ctx, chain); return 0; } EXPORT_SYMBOL_GPL(nft_chain_validate); @@ -4128,7 +4185,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) .net = net, .family = table->family, }; - int err; + int err = 0; list_for_each_entry(chain, &table->chains, list) { if (!nft_is_base_chain(chain)) @@ -4137,12 +4194,16 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) ctx.chain = chain; err = nft_chain_validate(&ctx, chain); if (err < 0) - return err; + goto err; cond_resched(); } - return 0; +err: + list_for_each_entry(chain, &table->chains, list) + memset(&chain->vstate, 0, sizeof(chain->vstate)); + + return err; } int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, From 7e7a817f2dfd79098a706ee5581ea9518b2de878 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 11 Dec 2025 12:55:19 +0100 Subject: [PATCH 061/214] netfilter: nf_tables: avoid softlockup warnings in nft_chain_validate This reverts commit 314c82841602 ("netfilter: nf_tables: can't schedule in nft_chain_validate"): Since commit a60a5abe19d6 ("netfilter: nf_tables: allow iter callbacks to sleep") the iterator callback is invoked without rcu read lock held, so this cond_resched() is now valid. Signed-off-by: Florian Westphal --- net/netfilter/nf_tables_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a9f6babcc781b..618af6e90773f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4171,6 +4171,8 @@ int nft_chain_validate(const struct nft_ctx *ctx, struct nft_chain *chain) if (err < 0) return err; } + + cond_resched(); } nft_chain_vstate_update(ctx, chain); @@ -4195,8 +4197,6 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) err = nft_chain_validate(&ctx, chain); if (err < 0) goto err; - - cond_resched(); } err: From fec7b0795548b43e2c3c46e3143c34ef6070341c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 11 Dec 2025 13:16:49 +0100 Subject: [PATCH 062/214] selftests: netfilter: packetdrill: avoid failure on HZ=100 kernel packetdrill --ip_version=ipv4 --mtu=1500 --tolerance_usecs=1000000 --non_fatal packet conntrack_syn_challenge_ack.pkt conntrack v1.4.8 (conntrack-tools): 1 flow entries have been shown. conntrack_syn_challenge_ack.pkt:32: error executing `conntrack -f $NFCT_IP_VERSION \ -L -p tcp --dport 8080 | grep UNREPLIED | grep -q SYN_SENT` command: non-zero status 1 Affected kernel had CONFIG_HZ=100; reset packet was still sitting in backlog. Reported-by: Yi Chen Fixes: a8a388c2aae4 ("selftests: netfilter: add packetdrill based conntrack tests") Signed-off-by: Florian Westphal --- .../net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt b/tools/testing/selftests/net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt index 3442cd29bc932..cdb3910af95b4 100644 --- a/tools/testing/selftests/net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt +++ b/tools/testing/selftests/net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt @@ -26,7 +26,7 @@ +0.01 > R 643160523:643160523(0) win 0 -+0.01 `conntrack -f $NFCT_IP_VERSION -L -p tcp --dport 8080 2>/dev/null | grep UNREPLIED | grep -q SYN_SENT` ++0.1 `conntrack -f $NFCT_IP_VERSION -L -p tcp --dport 8080 2>/dev/null | grep UNREPLIED | grep -q SYN_SENT` // Must go through. +0.01 > S 0:0(0) win 65535 From 193d18f60588e95d62e0f82b6a53893e5f2f19f8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 15 Dec 2025 17:11:34 +0200 Subject: [PATCH 063/214] wifi: mac80211: Discard Beacon frames to non-broadcast address Beacon frames are required to be sent to the broadcast address, see IEEE Std 802.11-2020, 11.1.3.1 ("The Address 1 field of the Beacon .. frame shall be set to the broadcast address"). A unicast Beacon frame might be used as a targeted attack to get one of the associated STAs to do something (e.g., using CSA to move it to another channel). As such, it is better have strict filtering for this on the received side and discard all Beacon frames that are sent to an unexpected address. This is even more important for cases where beacon protection is used. The current implementation in mac80211 is correctly discarding unicast Beacon frames if the Protected Frame bit in the Frame Control field is set to 0. However, if that bit is set to 1, the logic used for checking for configured BIGTK(s) does not actually work. If the driver does not have logic for dropping unicast Beacon frames with Protected Frame bit 1, these frames would be accepted in mac80211 processing as valid Beacon frames even though they are not protected. This would allow beacon protection to be bypassed. While the logic for checking beacon protection could be extended to cover this corner case, a more generic check for discard all Beacon frames based on A1=unicast address covers this without needing additional changes. Address all these issues by dropping received Beacon frames if they are sent to a non-broadcast address. Cc: stable@vger.kernel.org Fixes: af2d14b01c32 ("mac80211: Beacon protection using the new BIGTK (STA)") Signed-off-by: Jouni Malinen Link: https://patch.msgid.link/20251215151134.104501-1-jouni.malinen@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a1899512d078..e0ccd97498536 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3511,6 +3511,11 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) rx->skb->len < IEEE80211_MIN_ACTION_SIZE) return RX_DROP_U_RUNT_ACTION; + /* Drop non-broadcast Beacon frames */ + if (ieee80211_is_beacon(mgmt->frame_control) && + !is_broadcast_ether_addr(mgmt->da)) + return RX_DROP; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ieee80211_is_beacon(mgmt->frame_control) && !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { From cbf0dc37bb4e949f1c76566657e71f8e0bdcf338 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 4 Dec 2025 16:05:33 +0300 Subject: [PATCH 064/214] wifi: mac80211: fix list iteration in ieee80211_add_virtual_monitor() Since 'mon_list' of 'struct ieee80211_local' is RCU-protected and an instances of 'struct ieee80211_sub_if_data' are linked there via 'u.mntr.list' member, adjust the corresponding list iteration in 'ieee80211_add_virtual_monitor()' accordingly. Reported-by: syzbot+bc1aabf52d0a31e91f96@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=bc1aabf52d0a31e91f96 Fixes: a5aa46f1ac4f ("wifi: mac80211: track MU-MIMO configuration on disabled interfaces") Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20251204130533.340069-1-dmantipov@yandex.ru Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4f04d95c19d49..7b0aa24c1f97c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1251,7 +1251,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local, if (!creator_sdata) { struct ieee80211_sub_if_data *other; - list_for_each_entry(other, &local->mon_list, list) { + list_for_each_entry_rcu(other, &local->mon_list, u.mntr.list) { if (!other->vif.bss_conf.mu_mimo_owner) continue; From 2b77b9551d1184cb5af8271ff350e6e2c1b3db0d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Dec 2025 14:14:47 +0300 Subject: [PATCH 065/214] wifi: cfg80211: sme: store capped length in __cfg80211_connect_result() The QGenie AI code review tool says we should store the capped length to wdev->u.client.ssid_len. The AI is correct. Fixes: 62b635dcd69c ("wifi: cfg80211: sme: cap SSID length in __cfg80211_connect_result()") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/aTAbp5RleyH_lnZE@stanley.mountain Signed-off-by: Johannes Berg --- net/wireless/sme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3a028ff287fbb..4e629ca305bcc 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -910,7 +910,7 @@ void __cfg80211_connect_result(struct net_device *dev, ssid_len = min(ssid->datalen, IEEE80211_MAX_SSID_LEN); memcpy(wdev->u.client.ssid, ssid->data, ssid_len); - wdev->u.client.ssid_len = ssid->datalen; + wdev->u.client.ssid_len = ssid_len; break; } rcu_read_unlock(); From e75665dd096819b1184087ba5718bd93beafff51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20=C3=85strand?= Date: Wed, 3 Dec 2025 08:57:08 +0100 Subject: [PATCH 066/214] wifi: wlcore: ensure skb headroom before skb_push This avoids occasional skb_under_panic Oops from wl1271_tx_work. In this case, headroom is less than needed (typically 110 - 94 = 16 bytes). Signed-off-by: Peter Astrand Link: https://patch.msgid.link/097bd417-e1d7-acd4-be05-47b199075013@lysator.liu.se Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wlcore/tx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index f76087be2f758..6241866d39df6 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -207,6 +207,11 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks); if (total_blocks <= wl->tx_blocks_available) { + if (skb_headroom(skb) < (total_len - skb->len) && + pskb_expand_head(skb, (total_len - skb->len), 0, GFP_ATOMIC)) { + wl1271_free_tx_id(wl, id); + return -EAGAIN; + } desc = skb_push(skb, total_len - skb->len); wlcore_hw_set_tx_desc_blocks(wl, desc, total_blocks, From 99067b58a408a384d2a45c105eb3dce980a862ce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Dec 2025 10:25:11 +0100 Subject: [PATCH 067/214] wifi: mac80211: don't WARN for connections on invalid channels It's not clear (to me) how exactly syzbot managed to hit this, but it seems conceivable that e.g. regulatory changed and has disabled a channel between scanning (channel is checked to be usable by cfg80211_get_ies_channel_number) and connecting on the channel later. With one scenario that isn't covered elsewhere described above, the warning isn't good, replace it with a (more informative) error message. Reported-by: syzbot+639af5aa411f2581ad38@syzkaller.appspotmail.com Link: https://patch.msgid.link/20251202102511.5a8fb5184fa3.I961ee41b8f10538a54b8565dbf03ec1696e80e03@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e56ad4b9330f2..ad53dedd929c2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1126,7 +1126,10 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, while (!ieee80211_chandef_usable(sdata, &chanreq->oper, IEEE80211_CHAN_DISABLED)) { - if (WARN_ON(chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT)) { + if (chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT) { + link_id_info(sdata, link_id, + "unusable channel (%d MHz) for connection\n", + chanreq->oper.chan->center_freq); ret = -EINVAL; goto free; } From a519be2f5d958c5804f2cfd68f1f384291271fab Mon Sep 17 00:00:00 2001 From: Aloka Dixit Date: Mon, 15 Dec 2025 09:46:56 -0800 Subject: [PATCH 068/214] wifi: mac80211: do not use old MBSSID elements When userspace brings down and deletes a non-transmitted profile, it is expected to send a new updated Beacon template for the transmitted profile of that multiple BSSID (MBSSID) group which does not include the removed profile in MBSSID element. This update comes via NL80211_CMD_SET_BEACON. Such updates work well as long as the group continues to have at least one non-transmitted profile as NL80211_ATTR_MBSSID_ELEMS is included in the new Beacon template. But when the last non-trasmitted profile is removed, it still gets included in Beacon templates sent to driver. This happens because when no MBSSID elements are sent by the userspace, ieee80211_assign_beacon() ends up using the element stored from earlier Beacon template. Do not copy old MBSSID elements, instead userspace should always include these when applicable. Fixes: 2b3171c6fe0a ("mac80211: MBSSID beacon handling in AP mode") Signed-off-by: Aloka Dixit Link: https://patch.msgid.link/20251215174656.2866319-2-aloka.dixit@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b51c2c8584ae0..c81091a5cc3a3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1345,7 +1345,6 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, size = sizeof(*new) + new_head_len + new_tail_len; - /* new or old multiple BSSID elements? */ if (params->mbssid_ies) { mbssid = params->mbssid_ies; size += struct_size(new->mbssid_ies, elem, mbssid->cnt); @@ -1355,15 +1354,6 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, } size += ieee80211_get_mbssid_beacon_len(mbssid, rnr, mbssid->cnt); - } else if (old && old->mbssid_ies) { - mbssid = old->mbssid_ies; - size += struct_size(new->mbssid_ies, elem, mbssid->cnt); - if (old && old->rnr_ies) { - rnr = old->rnr_ies; - size += struct_size(new->rnr_ies, elem, rnr->cnt); - } - size += ieee80211_get_mbssid_beacon_len(mbssid, rnr, - mbssid->cnt); } new = kzalloc(size, GFP_KERNEL); From ff4071c60018a668249dc6a2df7d16330543540e Mon Sep 17 00:00:00 2001 From: Moon Hee Lee Date: Mon, 15 Dec 2025 19:59:32 -0800 Subject: [PATCH 069/214] wifi: mac80211: ocb: skip rx_no_sta when interface is not joined ieee80211_ocb_rx_no_sta() assumes a valid channel context, which is only present after JOIN_OCB. RX may run before JOIN_OCB is executed, in which case the OCB interface is not operational. Skip RX peer handling when the interface is not joined to avoid warnings in the RX path. Reported-by: syzbot+b364457b2d1d4e4a3054@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b364457b2d1d4e4a3054 Tested-by: syzbot+b364457b2d1d4e4a3054@syzkaller.appspotmail.com Signed-off-by: Moon Hee Lee Link: https://patch.msgid.link/20251216035932.18332-1-moonhee.lee.ca@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/ocb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index a5d4358f122ae..ebb4f4d88c237 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -47,6 +47,9 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; int band; + if (!ifocb->joined) + return; + /* XXX: Consider removing the least recently used entry and * allow new one to be added. */ From ca5898222914f399797cea1aeb0ce77109ca2e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 14 Nov 2025 00:28:52 +0200 Subject: [PATCH 070/214] wifi: iwlwifi: Fix firmware version handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On my system the arithmetic done on the firmware numbers results in a negative number, but since the types are unsigned it gets interpreted as a large positive number. The end result is that the firmware gets rejected and wifi is defunct. Switch to signed types to handle this case correctly. iwlwifi 0000:0c:00.0: Driver unable to support your firmware API. Driver supports FW core 4294967294..2, firmware is 2. iwlwifi 0000:0c:00.0: Direct firmware load for iwlwifi-5000-4.ucode failed with error -2 iwlwifi 0000:0c:00.0: Direct firmware load for iwlwifi-5000-3.ucode failed with error -2 iwlwifi 0000:0c:00.0: Direct firmware load for iwlwifi-5000-2.ucode failed with error -2 iwlwifi 0000:0c:00.0: Direct firmware load for iwlwifi-5000-1.ucode failed with error -2 iwlwifi 0000:0c:00.0: no suitable firmware found! iwlwifi 0000:0c:00.0: minimum version required: iwlwifi-5000-1 iwlwifi 0000:0c:00.0: maximum version supported: iwlwifi-5000-5 iwlwifi 0000:0c:00.0: check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git Cc: stable@vger.kernel.org Fixes: 5f708cccde9d ("wifi: iwlwifi: add a new FW file numbering scheme") Signed-off-by: Ville Syrjälä Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220805 Link: https://patch.msgid.link/20251113222852.15896-1-ville.syrjala@linux.intel.com Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 3391f07b01de3..f8fc6f30fbe5f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1597,7 +1597,7 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv) */ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) { - unsigned int min_core, max_core, loaded_core; + int min_core, max_core, loaded_core; struct iwl_drv *drv = context; struct iwl_fw *fw = &drv->fw; const struct iwl_ucode_header *ucode; @@ -1676,7 +1676,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) if (loaded_core < min_core || loaded_core > max_core) { IWL_ERR(drv, "Driver unable to support your firmware API. " - "Driver supports FW core %u..%u, firmware is %u.\n", + "Driver supports FW core %d..%d, firmware is %d.\n", min_core, max_core, loaded_core); goto try_again; } From 81d90d93d22ca4f61833cba921dce9a0bd82218f Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Thu, 4 Dec 2025 12:32:04 +0000 Subject: [PATCH 071/214] wifi: iwlwifi: Implement settime64 as stub for MVM/MLD PTP Since commit dfb073d32cac ("ptp: Return -EINVAL on ptp_clock_register if required ops are NULL"), PTP clock registered through ptp_clock_register is required to have ptp_clock_info.settime64 set, however, neither MVM nor MLD's PTP clock implementation sets it, resulting in warnings when the interface starts up, like WARNING: drivers/ptp/ptp_clock.c:325 at ptp_clock_register+0x2c8/0x6b8, CPU#1: wpa_supplicant/469 CPU: 1 UID: 0 PID: 469 Comm: wpa_supplicant Not tainted 6.18.0+ #101 PREEMPT(full) ra: ffff800002732cd4 iwl_mvm_ptp_init+0x114/0x188 [iwlmvm] ERA: 9000000002fdc468 ptp_clock_register+0x2c8/0x6b8 iwlwifi 0000:01:00.0: Failed to register PHC clock (-22) I don't find an appropriate firmware interface to implement settime64() for iwlwifi MLD/MVM, thus instead create a stub that returns -EOPTNOTSUPP only, suppressing the warning and allowing the PTP clock to be registered. Reported-by: Nathan Chancellor Closes: https://lore.kernel.org/all/20251108044822.GA3262936@ax162/ Signed-off-by: Yao Zi Tested-by: Nathan Chancellor Reviewed-by: Simon Horman tested-by: damian Tometzki damian@riscv-rocks.de Tested-by: Oliver Hartkopp Acked-by: Miri Korenblit Link: https://patch.msgid.link/20251204123204.9316-1-ziyao@disroot.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 7 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index ffeb37a7f830e..231920425c066 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -121,6 +121,12 @@ static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp, return 0; } +static int iwl_mld_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + return -EOPNOTSUPP; +} + static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct iwl_mld *mld = container_of(ptp, struct iwl_mld, @@ -279,6 +285,7 @@ void iwl_mld_ptp_init(struct iwl_mld *mld) mld->ptp_data.ptp_clock_info.owner = THIS_MODULE; mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime; + mld->ptp_data.ptp_clock_info.settime64 = iwl_mld_ptp_settime; mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime; mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index 06a4c9f74797a..ad156b82eaa94 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -220,6 +220,12 @@ static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp, return 0; } +static int iwl_mvm_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + return -EOPNOTSUPP; +} + static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, @@ -281,6 +287,7 @@ void iwl_mvm_ptp_init(struct iwl_mvm *mvm) mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine; mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime; mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime; + mvm->ptp_data.ptp_clock_info.settime64 = iwl_mvm_ptp_settime; mvm->ptp_data.scaled_freq = SCALE_FACTOR; /* Give a short 'friendly name' to identify the PHC clock */ From 05f5e355cf783b30bd6eb3dec17ed1a8b3cfa95c Mon Sep 17 00:00:00 2001 From: Bharath SM Date: Tue, 16 Dec 2025 21:26:05 +0530 Subject: [PATCH 072/214] smb: align durable reconnect v2 context to 8 byte boundary Add a 4-byte Pad to create_durable_handle_reconnect_v2 so the DH2C create context is 8 byte aligned. This avoids malformed CREATE contexts on reconnect. Recent change removed this Padding, adding it back. Fixes: 81a45de432c6 ("smb: move create_durable_handle_reconnect_v2 to common/smb2pdu.h") Signed-off-by: Bharath SM Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/common/smb2pdu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 3c8d8a4e74393..95323df7274b6 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1293,6 +1293,7 @@ struct create_durable_handle_reconnect_v2 { struct create_context_hdr ccontext; __u8 Name[8]; struct durable_reconnect_context_v2 dcontext; + __u8 Pad[4]; } __packed; /* See MS-SMB2 2.2.14.2.12 */ From 94d5b8dbc5d9caa8e01c8fab8d5ed56e843ff40e Mon Sep 17 00:00:00 2001 From: ZhangGuoDong Date: Tue, 2 Dec 2025 15:14:17 +0800 Subject: [PATCH 073/214] smb: move some SMB1 definitions into common/smb1pdu.h These definitions are only used by SMB1, so move them into the new common/smb1pdu.h. KSMBD only implements SMB_COM_NEGOTIATE, see MS-SMB2 3.3.5.2. Co-developed-by: ChenXiaoSong Signed-off-by: ChenXiaoSong Signed-off-by: ZhangGuoDong Signed-off-by: Steve French --- fs/smb/client/cifspdu.h | 2 +- fs/smb/common/smb1pdu.h | 56 ++++++++++++++++++++++++++++++++++++++ fs/smb/common/smb2pdu.h | 40 --------------------------- fs/smb/common/smbglob.h | 2 -- fs/smb/server/smb_common.h | 1 + 5 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 fs/smb/common/smb1pdu.h diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h index eeb4011cb217d..fdd84369e7b8b 100644 --- a/fs/smb/client/cifspdu.h +++ b/fs/smb/client/cifspdu.h @@ -12,7 +12,7 @@ #include #include #include "../common/smbfsctl.h" -#include "../common/smb2pdu.h" +#include "../common/smb1pdu.h" #define CIFS_PROT 0 #define POSIX_PROT (CIFS_PROT+1) diff --git a/fs/smb/common/smb1pdu.h b/fs/smb/common/smb1pdu.h new file mode 100644 index 0000000000000..df6d4e11ae929 --- /dev/null +++ b/fs/smb/common/smb1pdu.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (C) International Business Machines Corp., 2002,2009 + * 2018 Samsung Electronics Co., Ltd. + * Author(s): Steve French + * Namjae Jeon + * + */ + +#ifndef _COMMON_SMB1_PDU_H +#define _COMMON_SMB1_PDU_H + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) + +/* + * See MS-CIFS 2.2.3.1 + * MS-SMB 2.2.3.1 + */ +struct smb_hdr { + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __u16 Tid; + __le16 Pid; + __u16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +/* See MS-CIFS 2.2.4.52.1 */ +typedef struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[]; +} __packed SMB_NEGOTIATE_REQ; + +#endif /* _COMMON_SMB1_PDU_H */ diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 95323df7274b6..f5ebbe31384ae 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1986,39 +1986,6 @@ struct smb2_lease_ack { __le64 LeaseDuration; } __packed; -/* - * See MS-CIFS 2.2.3.1 - * MS-SMB 2.2.3.1 - */ -struct smb_hdr { - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __packed DosError; - __le32 CifsError; - } __packed Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __packed Sequence; - __u8 SecuritySignature[8]; /* le */ - } __packed Signature; - __u8 pad[2]; - __u16 Tid; - __le16 Pid; - __u16 Uid; - __le16 Mid; - __u8 WordCount; -} __packed; - #define OP_BREAK_STRUCT_SIZE_20 24 #define OP_BREAK_STRUCT_SIZE_21 36 @@ -2123,11 +2090,4 @@ struct smb_hdr { #define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ | READ_CONTROL | SYNCHRONIZE) -/* See MS-CIFS 2.2.4.52.1 */ -typedef struct smb_negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[]; -} __packed SMB_NEGOTIATE_REQ; - #endif /* _COMMON_SMB2PDU_H */ diff --git a/fs/smb/common/smbglob.h b/fs/smb/common/smbglob.h index 9562845a56175..4e33d91cdc9db 100644 --- a/fs/smb/common/smbglob.h +++ b/fs/smb/common/smbglob.h @@ -11,8 +11,6 @@ #ifndef _COMMON_SMB_GLOB_H #define _COMMON_SMB_GLOB_H -#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) - struct smb_version_values { char *version_string; __u16 protocol_id; diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index 067b45048c732..95bf1465387b9 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -10,6 +10,7 @@ #include "glob.h" #include "../common/smbglob.h" +#include "../common/smb1pdu.h" #include "../common/smb2pdu.h" #include "../common/fscc.h" #include "smb2pdu.h" From d8a4af8f3d9d3367b2c49b0d9dee529556bdd2f4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 13 Dec 2025 12:48:49 -0600 Subject: [PATCH 074/214] cifs: update internal module version number to 2.58 Signed-off-by: Steve French --- fs/smb/client/cifsfs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index e9534258d1efd..75d372ceb6553 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -145,6 +145,6 @@ extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 57 -#define CIFS_VERSION "2.57" +#define SMB3_PRODUCT_BUILD 58 +#define CIFS_VERSION "2.58" #endif /* _CIFSFS_H */ From 5d5602236f5db19e8b337a2cd87a90ace5ea776d Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 25 Nov 2025 22:39:59 +0900 Subject: [PATCH 075/214] can: j1939: make j1939_session_activate() fail if device is no longer registered syzbot is still reporting unregister_netdevice: waiting for vcan0 to become free. Usage count = 2 even after commit 93a27b5891b8 ("can: j1939: add missing calls in NETDEV_UNREGISTER notification handler") was added. A debug printk() patch found that j1939_session_activate() can succeed even after j1939_cancel_active_session() from j1939_netdev_notify(NETDEV_UNREGISTER) has completed. Since j1939_cancel_active_session() is processed with the session list lock held, checking ndev->reg_state in j1939_session_activate() with the session list lock held can reliably close the race window. Reported-by: syzbot Closes: https://syzkaller.appspot.com/bug?extid=881d65229ca4f9ae8c84 Signed-off-by: Tetsuo Handa Acked-by: Oleksij Rempel Link: https://patch.msgid.link/b9653191-d479-4c8b-8536-1326d028db5c@I-love.SAKURA.ne.jp Signed-off-by: Marc Kleine-Budde --- net/can/j1939/transport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index fbf5c8001c9df..613a911dda100 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -1567,6 +1567,8 @@ int j1939_session_activate(struct j1939_session *session) if (active) { j1939_session_put(active); ret = -EAGAIN; + } else if (priv->ndev->reg_state != NETREG_REGISTERED) { + ret = -ENODEV; } else { WARN_ON_ONCE(session->state != J1939_SESSION_NEW); list_add_tail(&session->active_session_list_entry, From 46cea215dc9444ec32a76b1b6a9cb809e17b64d5 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 25 Nov 2025 22:43:12 +0900 Subject: [PATCH 076/214] can: j1939: make j1939_sk_bind() fail if device is no longer registered There is a theoretical race window in j1939_sk_netdev_event_unregister() where two j1939_sk_bind() calls jump in between read_unlock_bh() and lock_sock(). The assumption jsk->priv == priv can fail if the first j1939_sk_bind() call once made jsk->priv == NULL due to failed j1939_local_ecu_get() call and the second j1939_sk_bind() call again made jsk->priv != NULL due to successful j1939_local_ecu_get() call. Since the socket lock is held by both j1939_sk_netdev_event_unregister() and j1939_sk_bind(), checking ndev->reg_state with the socket lock held can reliably make the second j1939_sk_bind() call fail (and close this race window). Fixes: 7fcbe5b2c6a4 ("can: j1939: implement NETDEV_UNREGISTER notification handler") Signed-off-by: Tetsuo Handa Acked-by: Oleksij Rempel Link: https://patch.msgid.link/5732921e-247e-4957-a364-da74bd7031d7@I-love.SAKURA.ne.jp Signed-off-by: Marc Kleine-Budde --- net/can/j1939/socket.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 6272326dd614a..ff9c4fd7b4337 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -482,6 +482,12 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr_unsized *uaddr, in goto out_release_sock; } + if (ndev->reg_state != NETREG_REGISTERED) { + dev_put(ndev); + ret = -ENODEV; + goto out_release_sock; + } + can_ml = can_get_ml_priv(ndev); if (!can_ml) { dev_put(ndev); From 5a5aff6338c0f4164a6a8d8a7eb400c4054df256 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 17 Dec 2025 10:45:53 +0100 Subject: [PATCH 077/214] can: fix build dependency Arnd Bergmann's patch [1] fixed the build dependency problem introduced by bugfix commit cb2dc6d2869a ("can: Kconfig: select CAN driver infrastructure by default"). This ended up as commit 6abd4577bccc ("can: fix build dependency"), but I broke Arnd's fix by removing a dependency that we thought was superfluous. [1] https://lore.kernel.org/all/20251204100015.1033688-1-arnd@kernel.org/ Meanwhile the problem was also found by intel's kernel test robot, complaining about undefined symbols: | ERROR: modpost: "m_can_class_unregister" [drivers/net/can/m_can/m_can_platform.ko] undefined! | ERROR: modpost: "m_can_class_free_dev" [drivers/net/can/m_can/m_can_platform.ko] undefined! | ERROR: modpost: "m_can_class_allocate_dev" [drivers/net/can/m_can/m_can_platform.ko] undefined! | ERROR: modpost: "m_can_class_get_clocks" [drivers/net/can/m_can/m_can_platform.ko] undefined! | ERROR: modpost: "m_can_class_register" [drivers/net/can/m_can/m_can_platform.ko] undefined! To fix this problem, add the missing dependency again. Cc: Vincent Mailhol Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512132253.vO9WFDJK-lkp@intel.com/ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512180808.fTAUQ2XN-lkp@intel.com/ Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/all/7427949a-ea7d-4854-9fe4-e01db7d878c7@app.fastmail.com/ Fixes: 6abd4577bccc ("can: fix build dependency") Fixes: cb2dc6d2869a ("can: Kconfig: select CAN driver infrastructure by default") Acked-by: Vincent Mailhol Link: https://patch.msgid.link/20251217-can-fix-dependency-v1-1-fd2d4f2a2bf5@pengutronix.de Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 460a74ae69233..cfaea6178a719 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -17,7 +17,7 @@ menuconfig CAN_DEV virtual ones. If you own such devices or plan to use the virtual CAN interfaces to develop applications, say Y here. -if CAN_DEV +if CAN_DEV && CAN config CAN_VCAN tristate "Virtual Local CAN Interface (vcan)" From 7b07be1ff1cb6c49869910518650e8d0abc7d25f Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 8 Dec 2025 14:19:01 +0200 Subject: [PATCH 078/214] ethtool: Avoid overflowing userspace buffer on stats query The ethtool -S command operates across three ioctl calls: ETHTOOL_GSSET_INFO for the size, ETHTOOL_GSTRINGS for the names, and ETHTOOL_GSTATS for the values. If the number of stats changes between these calls (e.g., due to device reconfiguration), userspace's buffer allocation will be incorrect, potentially leading to buffer overflow. Drivers are generally expected to maintain stable stat counts, but some drivers (e.g., mlx5, bnx2x, bna, ksz884x) use dynamic counters, making this scenario possible. Some drivers try to handle this internally: - bnad_get_ethtool_stats() returns early in case stats.n_stats is not equal to the driver's stats count. - micrel/ksz884x also makes sure not to write anything beyond stats.n_stats and overflow the buffer. However, both use stats.n_stats which is already assigned with the value returned from get_sset_count(), hence won't solve the issue described here. Change ethtool_get_strings(), ethtool_get_stats(), ethtool_get_phy_stats() to not return anything in case of a mismatch between userspace's size and get_sset_size(), to prevent buffer overflow. The returned n_stats value will be equal to zero, to reflect that nothing has been returned. This could result in one of two cases when using upstream ethtool, depending on when the size change is detected: 1. When detected in ethtool_get_strings(): # ethtool -S eth2 no stats available 2. When detected in get stats, all stats will be reported as zero. Both cases are presumably transient, and a subsequent ethtool call should succeed. Other than the overflow avoidance, these two cases are very evident (no output/cleared stats), which is arguably better than presenting incorrect/shifted stats. I also considered returning an error instead of a "silent" response, but that seems more destructive towards userspace apps. Notes: - This patch does not claim to fix the inherent race, it only makes sure that we do not overflow the userspace buffer, and makes for a more predictable behavior. - RTNL lock is held during each ioctl, the race window exists between the separate ioctl calls when the lock is released. - Userspace ethtool always fills stats.n_stats, but it is likely that these stats ioctls are implemented in other userspace applications which might not fill it. The added code checks that it's not zero, to prevent any regressions. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Dragos Tatulea Reviewed-by: Tariq Toukan Signed-off-by: Gal Pressman Link: https://patch.msgid.link/20251208121901.3203692-1-gal@nvidia.com Signed-off-by: Paolo Abeni --- net/ethtool/ioctl.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index fa83ddade4f81..9431e305b2333 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2383,7 +2383,10 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) return -ENOMEM; WARN_ON_ONCE(!ret); - gstrings.len = ret; + if (gstrings.len && gstrings.len != ret) + gstrings.len = 0; + else + gstrings.len = ret; if (gstrings.len) { data = vzalloc(array_size(gstrings.len, ETH_GSTRING_LEN)); @@ -2509,10 +2512,13 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) if (copy_from_user(&stats, useraddr, sizeof(stats))) return -EFAULT; - stats.n_stats = n_stats; + if (stats.n_stats && stats.n_stats != n_stats) + stats.n_stats = 0; + else + stats.n_stats = n_stats; - if (n_stats) { - data = vzalloc(array_size(n_stats, sizeof(u64))); + if (stats.n_stats) { + data = vzalloc(array_size(stats.n_stats, sizeof(u64))); if (!data) return -ENOMEM; ops->get_ethtool_stats(dev, &stats, data); @@ -2524,7 +2530,9 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) if (copy_to_user(useraddr, &stats, sizeof(stats))) goto out; useraddr += sizeof(stats); - if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64)))) + if (stats.n_stats && + copy_to_user(useraddr, data, + array_size(stats.n_stats, sizeof(u64)))) goto out; ret = 0; @@ -2560,6 +2568,10 @@ static int ethtool_get_phy_stats_phydev(struct phy_device *phydev, return -EOPNOTSUPP; n_stats = phy_ops->get_sset_count(phydev); + if (stats->n_stats && stats->n_stats != n_stats) { + stats->n_stats = 0; + return 0; + } ret = ethtool_vzalloc_stats_array(n_stats, data); if (ret) @@ -2580,6 +2592,10 @@ static int ethtool_get_phy_stats_ethtool(struct net_device *dev, return -EOPNOTSUPP; n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS); + if (stats->n_stats && stats->n_stats != n_stats) { + stats->n_stats = 0; + return 0; + } ret = ethtool_vzalloc_stats_array(n_stats, data); if (ret) @@ -2616,7 +2632,9 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) } useraddr += sizeof(stats); - if (copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof(u64)))) + if (stats.n_stats && + copy_to_user(useraddr, data, + array_size(stats.n_stats, sizeof(u64)))) ret = -EFAULT; out: From 377d66fa86654085be1f48906c1d88b7ca721c78 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 9 Dec 2025 01:28:20 +0000 Subject: [PATCH 079/214] net: dsa: lantiq_gswip: fix order in .remove operation Russell King pointed out that disabling the switch by clearing GSWIP_MDIO_GLOB_ENABLE before calling dsa_unregister_switch() is problematic, as it violates a Golden Rule of driver development to always first unpublish userspace interfaces and then disable the hardware. Fix this, and also simplify the probe() function, by introducing a dsa_switch_ops teardown() operation which takes care of clearing the GSWIP_MDIO_GLOB_ENABLE bit. Fixes: 14fceff4771e5 ("net: dsa: Add Lantiq / Intel DSA driver for vrx200") Suggested-by: "Russell King (Oracle)" Signed-off-by: Daniel Golle Link: https://patch.msgid.link/4ebd72a29edc1e4059b9666a26a0bb5d906a829a.1765241054.git.daniel@makrotopia.org Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/lantiq/lantiq_gswip.c | 3 --- drivers/net/dsa/lantiq/lantiq_gswip_common.c | 13 ++++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.c b/drivers/net/dsa/lantiq/lantiq_gswip.c index 57dd063c07403..b094001a7c805 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq/lantiq_gswip.c @@ -444,9 +444,6 @@ static void gswip_remove(struct platform_device *pdev) if (!priv) return; - /* disable the switch */ - gswip_disable_switch(priv); - dsa_unregister_switch(priv->ds); for (i = 0; i < priv->num_gphy_fw; i++) diff --git a/drivers/net/dsa/lantiq/lantiq_gswip_common.c b/drivers/net/dsa/lantiq/lantiq_gswip_common.c index 9da39edf8f574..6b171d58e1862 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip_common.c +++ b/drivers/net/dsa/lantiq/lantiq_gswip_common.c @@ -752,6 +752,13 @@ static int gswip_setup(struct dsa_switch *ds) return 0; } +static void gswip_teardown(struct dsa_switch *ds) +{ + struct gswip_priv *priv = ds->priv; + + regmap_clear_bits(priv->mdio, GSWIP_MDIO_GLOB, GSWIP_MDIO_GLOB_ENABLE); +} + static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -1629,6 +1636,7 @@ static const struct phylink_mac_ops gswip_phylink_mac_ops = { static const struct dsa_switch_ops gswip_switch_ops = { .get_tag_protocol = gswip_get_tag_protocol, .setup = gswip_setup, + .teardown = gswip_teardown, .port_setup = gswip_port_setup, .port_enable = gswip_port_enable, .port_disable = gswip_port_disable, @@ -1718,15 +1726,14 @@ int gswip_probe_common(struct gswip_priv *priv, u32 version) err = gswip_validate_cpu_port(priv->ds); if (err) - goto disable_switch; + goto unregister_switch; dev_info(priv->dev, "probed GSWIP version %lx mod %lx\n", GSWIP_VERSION_REV(version), GSWIP_VERSION_MOD(version)); return 0; -disable_switch: - gswip_disable_switch(priv); +unregister_switch: dsa_unregister_switch(priv->ds); return err; From 8e4c0f08f6bedeb885515c5ec5a6388a45d768ec Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 9 Dec 2025 01:28:49 +0000 Subject: [PATCH 080/214] net: dsa: mxl-gsw1xx: fix order in .remove operation The driver's .remove operation was calling gswip_disable_switch() which clears the GSWIP_MDIO_GLOB_ENABLE bit before calling dsa_unregister_switch() and thereby violating a Golden Rule of driver development to always unpublish userspace interfaces before disabling hardware, as pointed out by Russell King. Fix this by relying in GSWIP_MDIO_GLOB_ENABLE being cleared by the .teardown operation introduced by the previous commit ("net: dsa: lantiq_gswip: fix teardown order"). Fixes: 22335939ec907 ("net: dsa: add driver for MaxLinear GSW1xx switch family") Suggested-by: "Russell King (Oracle)" Signed-off-by: Daniel Golle Link: https://patch.msgid.link/63f882eeb910cf24503c35a443b541cc54a930f2.1765241054.git.daniel@makrotopia.org Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/lantiq/mxl-gsw1xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c index cf33a16fd183b..cda966d71e889 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.c +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c @@ -652,8 +652,6 @@ static void gsw1xx_remove(struct mdio_device *mdiodev) if (!priv) return; - gswip_disable_switch(priv); - dsa_unregister_switch(priv->ds); } From 651b253b80379b0eb3669405fcf50d4039dc7a0e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 9 Dec 2025 01:29:05 +0000 Subject: [PATCH 081/214] net: dsa: mxl-gsw1xx: fix .shutdown driver operation The .shutdown operation should call dsa_switch_shutdown() just like it is done also by the sibling lantiq_gswip driver. Not doing that results in shutdown or reboot hanging and waiting for the CPU port becoming free, which introduces a longer delay and a WARNING before shutdown or reboot in case the driver is built-into the kernel. Fix this by calling dsa_switch_shutdown() in the driver's shutdown operation, harmonizing it with what is done in the lantiq_gswip driver. As a side-effect this now allows to remove the previously exported gswip_disable_switch() function which no longer got any users. Fixes: 22335939ec907 ("net: dsa: add driver for MaxLinear GSW1xx switch family") Signed-off-by: Daniel Golle Link: https://patch.msgid.link/77ed91a5206e5dbf5d3e83d7e364ebfda90d31fd.1765241054.git.daniel@makrotopia.org Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/lantiq/lantiq_gswip.h | 2 -- drivers/net/dsa/lantiq/lantiq_gswip_common.c | 6 ------ drivers/net/dsa/lantiq/mxl-gsw1xx.c | 4 ++-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.h b/drivers/net/dsa/lantiq/lantiq_gswip.h index 9c38e51a75e80..2e0f2afbadbbc 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip.h +++ b/drivers/net/dsa/lantiq/lantiq_gswip.h @@ -294,8 +294,6 @@ struct gswip_priv { u16 version; }; -void gswip_disable_switch(struct gswip_priv *priv); - int gswip_probe_common(struct gswip_priv *priv, u32 version); #endif /* __LANTIQ_GSWIP_H */ diff --git a/drivers/net/dsa/lantiq/lantiq_gswip_common.c b/drivers/net/dsa/lantiq/lantiq_gswip_common.c index 6b171d58e1862..e790f2ef75884 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip_common.c +++ b/drivers/net/dsa/lantiq/lantiq_gswip_common.c @@ -1664,12 +1664,6 @@ static const struct dsa_switch_ops gswip_switch_ops = { .port_hsr_leave = dsa_port_simple_hsr_leave, }; -void gswip_disable_switch(struct gswip_priv *priv) -{ - regmap_clear_bits(priv->mdio, GSWIP_MDIO_GLOB, GSWIP_MDIO_GLOB_ENABLE); -} -EXPORT_SYMBOL_GPL(gswip_disable_switch); - static int gswip_validate_cpu_port(struct dsa_switch *ds) { struct gswip_priv *priv = ds->priv; diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c index cda966d71e889..4dc287ad141e1 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.c +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c @@ -662,9 +662,9 @@ static void gsw1xx_shutdown(struct mdio_device *mdiodev) if (!priv) return; - dev_set_drvdata(&mdiodev->dev, NULL); + dsa_switch_shutdown(priv->ds); - gswip_disable_switch(priv); + dev_set_drvdata(&mdiodev->dev, NULL); } static const struct gswip_hw_info gsw12x_data = { From 7b103aaf0d564b83ee1d4bb532ee7ae36ed001ed Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 9 Dec 2025 01:29:34 +0000 Subject: [PATCH 082/214] net: dsa: mxl-gsw1xx: manually clear RANEG bit Despite being documented as self-clearing, the RANEG bit sometimes remains set, preventing auto-negotiation from happening. Manually clear the RANEG bit after 10ms as advised by MaxLinear. In order to not hold RTNL during the 10ms of waiting schedule delayed work to take care of clearing the bit asynchronously, which is similar to the self-clearing behavior. Fixes: 22335939ec90 ("net: dsa: add driver for MaxLinear GSW1xx switch family") Reported-by: Rasmus Villemoes Signed-off-by: Daniel Golle Link: https://patch.msgid.link/76745fceb5a3f53088110fb7a96acf88434088ca.1765241054.git.daniel@makrotopia.org Reviewed-by: Vladimir Oltean Signed-off-by: Paolo Abeni --- drivers/net/dsa/lantiq/mxl-gsw1xx.c | 34 ++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c index 4dc287ad141e1..f8ff8a604bf53 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.c +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c @@ -11,10 +11,12 @@ #include #include +#include #include #include #include #include +#include #include #include "lantiq_gswip.h" @@ -29,6 +31,7 @@ struct gsw1xx_priv { struct regmap *clk; struct regmap *shell; struct phylink_pcs pcs; + struct delayed_work clear_raneg; phy_interface_t tbi_interface; struct gswip_priv gswip; }; @@ -145,7 +148,9 @@ static void gsw1xx_pcs_disable(struct phylink_pcs *pcs) { struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs); - /* Assert SGMII shell reset */ + cancel_delayed_work_sync(&priv->clear_raneg); + + /* Assert SGMII shell reset (will also clear RANEG bit) */ regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ, GSW1XX_RST_REQ_SGMII_SHELL); @@ -428,12 +433,29 @@ static int gsw1xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, return 0; } +static void gsw1xx_pcs_clear_raneg(struct work_struct *work) +{ + struct gsw1xx_priv *priv = + container_of(work, struct gsw1xx_priv, clear_raneg.work); + + regmap_clear_bits(priv->sgmii, GSW1XX_SGMII_TBI_ANEGCTL, + GSW1XX_SGMII_TBI_ANEGCTL_RANEG); +} + static void gsw1xx_pcs_an_restart(struct phylink_pcs *pcs) { struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs); + cancel_delayed_work_sync(&priv->clear_raneg); + regmap_set_bits(priv->sgmii, GSW1XX_SGMII_TBI_ANEGCTL, GSW1XX_SGMII_TBI_ANEGCTL_RANEG); + + /* despite being documented as self-clearing, the RANEG bit + * sometimes remains set, preventing auto-negotiation from happening. + * MaxLinear advises to manually clear the bit after 10ms. + */ + schedule_delayed_work(&priv->clear_raneg, msecs_to_jiffies(10)); } static void gsw1xx_pcs_link_up(struct phylink_pcs *pcs, @@ -636,6 +658,8 @@ static int gsw1xx_probe(struct mdio_device *mdiodev) if (ret) return ret; + INIT_DELAYED_WORK(&priv->clear_raneg, gsw1xx_pcs_clear_raneg); + ret = gswip_probe_common(&priv->gswip, version); if (ret) return ret; @@ -648,16 +672,21 @@ static int gsw1xx_probe(struct mdio_device *mdiodev) static void gsw1xx_remove(struct mdio_device *mdiodev) { struct gswip_priv *priv = dev_get_drvdata(&mdiodev->dev); + struct gsw1xx_priv *gsw1xx_priv; if (!priv) return; dsa_unregister_switch(priv->ds); + + gsw1xx_priv = container_of(priv, struct gsw1xx_priv, gswip); + cancel_delayed_work_sync(&gsw1xx_priv->clear_raneg); } static void gsw1xx_shutdown(struct mdio_device *mdiodev) { struct gswip_priv *priv = dev_get_drvdata(&mdiodev->dev); + struct gsw1xx_priv *gsw1xx_priv; if (!priv) return; @@ -665,6 +694,9 @@ static void gsw1xx_shutdown(struct mdio_device *mdiodev) dsa_switch_shutdown(priv->ds); dev_set_drvdata(&mdiodev->dev, NULL); + + gsw1xx_priv = container_of(priv, struct gsw1xx_priv, gswip); + cancel_delayed_work_sync(&gsw1xx_priv->clear_raneg); } static const struct gswip_hw_info gsw12x_data = { From 89a898d63f6f588acf5c104c65c94a38b68c69a6 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Tue, 9 Dec 2025 14:56:09 +0200 Subject: [PATCH 083/214] net/mlx5: fw reset, clear reset requested on drain_fw_reset drain_fw_reset() waits for ongoing firmware reset events and blocks new event handling, but does not clear the reset requested flag, and may keep sync reset polling. To fix it, call mlx5_sync_reset_clear_reset_requested() to clear the flag, stop sync reset polling, and resume health polling, ensuring health issues are still detected after the firmware reset drain. Fixes: 16d42d313350 ("net/mlx5: Drain fw_reset when removing device") Signed-off-by: Moshe Shemesh Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-2-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c index 2bceb42c98cc2..b81de792c181a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c @@ -844,7 +844,8 @@ void mlx5_drain_fw_reset(struct mlx5_core_dev *dev) cancel_work_sync(&fw_reset->reset_reload_work); cancel_work_sync(&fw_reset->reset_now_work); cancel_work_sync(&fw_reset->reset_abort_work); - cancel_delayed_work(&fw_reset->reset_timeout_work); + if (test_bit(MLX5_FW_RESET_FLAGS_RESET_REQUESTED, &fw_reset->reset_flags)) + mlx5_sync_reset_clear_reset_requested(dev, true); } static const struct devlink_param mlx5_fw_reset_devlink_params[] = { From 5846a365fc6476b02d6766963cf0985520f0385f Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Tue, 9 Dec 2025 14:56:10 +0200 Subject: [PATCH 084/214] net/mlx5: Drain firmware reset in shutdown callback Invoke drain_fw_reset() in the shutdown callback to ensure all firmware reset handling is completed before shutdown proceeds. Fixes: 16d42d313350 ("net/mlx5: Drain fw_reset when removing device") Signed-off-by: Moshe Shemesh Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-3-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 024339ce41f19..cf53affe61ce2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -2232,6 +2232,7 @@ static void shutdown(struct pci_dev *pdev) mlx5_core_info(dev, "Shutdown was called\n"); set_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state); + mlx5_drain_fw_reset(dev); mlx5_drain_health_wq(dev); err = mlx5_try_fast_unload(dev); if (err) From b35966042d20b14e2d83330049f77deec5229749 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 9 Dec 2025 14:56:11 +0200 Subject: [PATCH 085/214] net/mlx5: fw_tracer, Validate format string parameters Add validation for format string parameters in the firmware tracer to prevent potential security vulnerabilities and crashes from malformed format strings received from firmware. The firmware tracer receives format strings from the device firmware and uses them to format trace messages. Without proper validation, bad firmware could provide format strings with invalid format specifiers (e.g., %s, %p, %n) that could lead to crashes, or other undefined behavior. Add mlx5_tracer_validate_params() to validate that all format specifiers in trace strings are limited to safe integer/hex formats (%x, %d, %i, %u, %llx, %lx, etc.). Reject strings containing other format types that could be used to access arbitrary memory or cause crashes. Invalid format strings are added to the trace output for visibility with "BAD_FORMAT: " prefix. Fixes: 70dd6fdb8987 ("net/mlx5: FW tracer, parse traces and kernel tracing support") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Reported-by: Breno Leitao Closes: https://lore.kernel.org/netdev/hanz6rzrb2bqbplryjrakvkbmv4y5jlmtthnvi3thg5slqvelp@t3s3erottr6s/ Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-4-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../mellanox/mlx5/core/diag/fw_tracer.c | 83 ++++++++++++++++--- .../mellanox/mlx5/core/diag/fw_tracer.h | 1 + 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index 7bcf822a89f9f..b415dfe5de45f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -33,6 +33,7 @@ #include "lib/eq.h" #include "fw_tracer.h" #include "fw_tracer_tracepoint.h" +#include static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer) { @@ -358,6 +359,43 @@ static const char *VAL_PARM = "%llx"; static const char *REPLACE_64_VAL_PARM = "%x%x"; static const char *PARAM_CHAR = "%"; +static bool mlx5_is_valid_spec(const char *str) +{ + /* Parse format specifiers to find the actual type. + * Structure: %[flags][width][.precision][length]type + * Skip flags, width, precision & length. + */ + while (isdigit(*str) || *str == '#' || *str == '.' || *str == 'l') + str++; + + /* Check if it's a valid integer/hex specifier: + * Valid formats: %x, %d, %i, %u, etc. + */ + if (*str != 'x' && *str != 'X' && *str != 'd' && *str != 'i' && + *str != 'u' && *str != 'c') + return false; + + return true; +} + +static bool mlx5_tracer_validate_params(const char *str) +{ + const char *substr = str; + + if (!str) + return false; + + substr = strstr(substr, PARAM_CHAR); + while (substr) { + if (!mlx5_is_valid_spec(substr + 1)) + return false; + + substr = strstr(substr + 1, PARAM_CHAR); + } + + return true; +} + static int mlx5_tracer_message_hash(u32 message_id) { return jhash_1word(message_id, 0) & (MESSAGE_HASH_SIZE - 1); @@ -419,6 +457,10 @@ static int mlx5_tracer_get_num_of_params(char *str) char *substr, *pstr = str; int num_of_params = 0; + /* Validate that all parameters are valid before processing */ + if (!mlx5_tracer_validate_params(str)) + return -EINVAL; + /* replace %llx with %x%x */ substr = strstr(pstr, VAL_PARM); while (substr) { @@ -570,14 +612,17 @@ void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt, { char tmp[512]; - snprintf(tmp, sizeof(tmp), str_frmt->string, - str_frmt->params[0], - str_frmt->params[1], - str_frmt->params[2], - str_frmt->params[3], - str_frmt->params[4], - str_frmt->params[5], - str_frmt->params[6]); + if (str_frmt->invalid_string) + snprintf(tmp, sizeof(tmp), "BAD_FORMAT: %s", str_frmt->string); + else + snprintf(tmp, sizeof(tmp), str_frmt->string, + str_frmt->params[0], + str_frmt->params[1], + str_frmt->params[2], + str_frmt->params[3], + str_frmt->params[4], + str_frmt->params[5], + str_frmt->params[6]); trace_mlx5_fw(dev->tracer, trace_timestamp, str_frmt->lost, str_frmt->event_id, tmp); @@ -609,6 +654,13 @@ static int mlx5_tracer_handle_raw_string(struct mlx5_fw_tracer *tracer, return 0; } +static void mlx5_tracer_handle_bad_format_string(struct mlx5_fw_tracer *tracer, + struct tracer_string_format *cur_string) +{ + cur_string->invalid_string = true; + list_add_tail(&cur_string->list, &tracer->ready_strings_list); +} + static int mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer *tracer, struct tracer_event *tracer_event) { @@ -619,12 +671,18 @@ static int mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer *tracer, if (!cur_string) return mlx5_tracer_handle_raw_string(tracer, tracer_event); - cur_string->num_of_params = mlx5_tracer_get_num_of_params(cur_string->string); - cur_string->last_param_num = 0; cur_string->event_id = tracer_event->event_id; cur_string->tmsn = tracer_event->string_event.tmsn; cur_string->timestamp = tracer_event->string_event.timestamp; cur_string->lost = tracer_event->lost_event; + cur_string->last_param_num = 0; + cur_string->num_of_params = mlx5_tracer_get_num_of_params(cur_string->string); + if (cur_string->num_of_params < 0) { + pr_debug("%s Invalid format string parameters\n", + __func__); + mlx5_tracer_handle_bad_format_string(tracer, cur_string); + return 0; + } if (cur_string->num_of_params == 0) /* trace with no params */ list_add_tail(&cur_string->list, &tracer->ready_strings_list); } else { @@ -634,6 +692,11 @@ static int mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer *tracer, __func__, tracer_event->string_event.tmsn); return mlx5_tracer_handle_raw_string(tracer, tracer_event); } + if (cur_string->num_of_params < 0) { + pr_debug("%s string parameter of invalid string, dumping\n", + __func__); + return 0; + } cur_string->last_param_num += 1; if (cur_string->last_param_num > TRACER_MAX_PARAMS) { pr_debug("%s Number of params exceeds the max (%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h index 5c548bb74f07b..30d0bcba88479 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h @@ -125,6 +125,7 @@ struct tracer_string_format { struct list_head list; u32 timestamp; bool lost; + bool invalid_string; }; enum mlx5_fw_tracer_ownership_state { From c0289f67f7d6a0dfba0e92cfe661a5c70c8c6e92 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 9 Dec 2025 14:56:12 +0200 Subject: [PATCH 086/214] net/mlx5: fw_tracer, Handle escaped percent properly The firmware tracer's format string validation and parameter counting did not properly handle escaped percent signs (%%). This caused fw_tracer to count more parameters when trace format strings contained literal percent characters. To fix it, allow %% to pass string validation and skip %% sequences when counting parameters since they represent literal percent signs rather than format specifiers. Fixes: 70dd6fdb8987 ("net/mlx5: FW tracer, parse traces and kernel tracing support") Signed-off-by: Shay Drory Reported-by: Breno Leitao Reviewed-by: Moshe Shemesh Closes: https://lore.kernel.org/netdev/hanz6rzrb2bqbplryjrakvkbmv4y5jlmtthnvi3thg5slqvelp@t3s3erottr6s/ Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-5-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../mellanox/mlx5/core/diag/fw_tracer.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index b415dfe5de45f..6b4ec457ce227 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -368,11 +368,11 @@ static bool mlx5_is_valid_spec(const char *str) while (isdigit(*str) || *str == '#' || *str == '.' || *str == 'l') str++; - /* Check if it's a valid integer/hex specifier: + /* Check if it's a valid integer/hex specifier or %%: * Valid formats: %x, %d, %i, %u, etc. */ if (*str != 'x' && *str != 'X' && *str != 'd' && *str != 'i' && - *str != 'u' && *str != 'c') + *str != 'u' && *str != 'c' && *str != '%') return false; return true; @@ -390,7 +390,11 @@ static bool mlx5_tracer_validate_params(const char *str) if (!mlx5_is_valid_spec(substr + 1)) return false; - substr = strstr(substr + 1, PARAM_CHAR); + if (*(substr + 1) == '%') + substr = strstr(substr + 2, PARAM_CHAR); + else + substr = strstr(substr + 1, PARAM_CHAR); + } return true; @@ -469,11 +473,15 @@ static int mlx5_tracer_get_num_of_params(char *str) substr = strstr(pstr, VAL_PARM); } - /* count all the % characters */ + /* count all the % characters, but skip %% (escaped percent) */ substr = strstr(str, PARAM_CHAR); while (substr) { - num_of_params += 1; - str = substr + 1; + if (*(substr + 1) != '%') { + num_of_params += 1; + str = substr + 1; + } else { + str = substr + 2; + } substr = strstr(str, PARAM_CHAR); } From 367e501f8b095eca08d2eb0ba4ccea5b5e82c169 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 9 Dec 2025 14:56:13 +0200 Subject: [PATCH 087/214] net/mlx5: Serialize firmware reset with devlink The firmware reset mechanism can be triggered by asynchronous events, which may race with other devlink operations like devlink reload or devlink dev eswitch set, potentially leading to inconsistent states. This patch addresses the race by using the devl_lock to serialize the firmware reset against other devlink operations. When a reset is requested, the driver attempts to acquire the lock. If successful, it sets a flag to block devlink reload or eswitch changes, ACKs the reset to firmware and then releases the lock. If the lock is already held by another operation, the driver NACKs the firmware reset request, indicating that the reset cannot proceed. Firmware reset does not keep the devl_lock and instead uses an internal firmware reset bit. This is because firmware resets can be triggered by asynchronous events, and processed in different threads. It is illegal and unsafe to acquire a lock in one thread and attempt to release it in another, as lock ownership is intrinsically thread-specific. This change ensures that firmware resets and other devlink operations are mutually exclusive during the critical reset request phase, preventing race conditions. Fixes: 38b9f903f22b ("net/mlx5: Handle sync reset request event") Signed-off-by: Shay Drory Reviewed-by: Mateusz Berezecki Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-6-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../net/ethernet/mellanox/mlx5/core/devlink.c | 5 +++ .../mellanox/mlx5/core/eswitch_offloads.c | 6 +++ .../ethernet/mellanox/mlx5/core/fw_reset.c | 45 +++++++++++++++++-- .../ethernet/mellanox/mlx5/core/fw_reset.h | 1 + 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 887adf4807d16..ea77fbd98396a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -197,6 +197,11 @@ static int mlx5_devlink_reload_down(struct devlink *devlink, bool netns_change, struct pci_dev *pdev = dev->pdev; int ret = 0; + if (mlx5_fw_reset_in_progress(dev)) { + NL_SET_ERR_MSG_MOD(extack, "Can't reload during firmware reset"); + return -EBUSY; + } + if (mlx5_dev_is_lightweight(dev)) { if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 8de6c7f6c2944..ea94a727633f1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -52,6 +52,7 @@ #include "devlink.h" #include "lag/lag.h" #include "en/tc/post_meter.h" +#include "fw_reset.h" /* There are two match-all miss flows, one for unicast dst mac and * one for multicast. @@ -3991,6 +3992,11 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, if (IS_ERR(esw)) return PTR_ERR(esw); + if (mlx5_fw_reset_in_progress(esw->dev)) { + NL_SET_ERR_MSG_MOD(extack, "Can't change eswitch mode during firmware reset"); + return -EBUSY; + } + if (esw_mode_from_devlink(mode, &mlx5_mode)) return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c index b81de792c181a..ae10665c53f32 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c @@ -15,6 +15,7 @@ enum { MLX5_FW_RESET_FLAGS_DROP_NEW_REQUESTS, MLX5_FW_RESET_FLAGS_RELOAD_REQUIRED, MLX5_FW_RESET_FLAGS_UNLOAD_EVENT, + MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, }; struct mlx5_fw_reset { @@ -128,6 +129,16 @@ int mlx5_fw_reset_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_ty return mlx5_reg_mfrl_query(dev, reset_level, reset_type, NULL, NULL); } +bool mlx5_fw_reset_in_progress(struct mlx5_core_dev *dev) +{ + struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset; + + if (!fw_reset) + return false; + + return test_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags); +} + static int mlx5_fw_reset_get_reset_method(struct mlx5_core_dev *dev, u8 *reset_method) { @@ -243,6 +254,8 @@ static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev) BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE)); devl_unlock(devlink); } + + clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags); } static void mlx5_stop_sync_reset_poll(struct mlx5_core_dev *dev) @@ -462,27 +475,48 @@ static void mlx5_sync_reset_request_event(struct work_struct *work) struct mlx5_fw_reset *fw_reset = container_of(work, struct mlx5_fw_reset, reset_request_work); struct mlx5_core_dev *dev = fw_reset->dev; + bool nack_request = false; + struct devlink *devlink; int err; err = mlx5_fw_reset_get_reset_method(dev, &fw_reset->reset_method); - if (err) + if (err) { + nack_request = true; mlx5_core_warn(dev, "Failed reading MFRL, err %d\n", err); + } else if (!mlx5_is_reset_now_capable(dev, fw_reset->reset_method) || + test_bit(MLX5_FW_RESET_FLAGS_NACK_RESET_REQUEST, + &fw_reset->reset_flags)) { + nack_request = true; + } - if (err || test_bit(MLX5_FW_RESET_FLAGS_NACK_RESET_REQUEST, &fw_reset->reset_flags) || - !mlx5_is_reset_now_capable(dev, fw_reset->reset_method)) { + devlink = priv_to_devlink(dev); + /* For external resets, try to acquire devl_lock. Skip if devlink reset is + * pending (lock already held) + */ + if (nack_request || + (!test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, + &fw_reset->reset_flags) && + !devl_trylock(devlink))) { err = mlx5_fw_reset_set_reset_sync_nack(dev); mlx5_core_warn(dev, "PCI Sync FW Update Reset Nack %s", err ? "Failed" : "Sent"); return; } + if (mlx5_sync_reset_set_reset_requested(dev)) - return; + goto unlock; + + set_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags); err = mlx5_fw_reset_set_reset_sync_ack(dev); if (err) mlx5_core_warn(dev, "PCI Sync FW Update Reset Ack Failed. Error code: %d\n", err); else mlx5_core_warn(dev, "PCI Sync FW Update Reset Ack. Device reset is expected.\n"); + +unlock: + if (!test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) + devl_unlock(devlink); } static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev, u16 dev_id) @@ -722,6 +756,8 @@ static void mlx5_sync_reset_abort_event(struct work_struct *work) if (mlx5_sync_reset_clear_reset_requested(dev, true)) return; + + clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags); mlx5_core_warn(dev, "PCI Sync FW Update Reset Aborted.\n"); } @@ -758,6 +794,7 @@ static void mlx5_sync_reset_timeout_work(struct work_struct *work) if (mlx5_sync_reset_clear_reset_requested(dev, true)) return; + clear_bit(MLX5_FW_RESET_FLAGS_RESET_IN_PROGRESS, &fw_reset->reset_flags); mlx5_core_warn(dev, "PCI Sync FW Update Reset Timeout.\n"); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h index d5b28525c960d..2d96b2adc1cdf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h @@ -10,6 +10,7 @@ int mlx5_fw_reset_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_ty int mlx5_fw_reset_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel, struct netlink_ext_ack *extack); int mlx5_fw_reset_set_live_patch(struct mlx5_core_dev *dev); +bool mlx5_fw_reset_in_progress(struct mlx5_core_dev *dev); int mlx5_fw_reset_wait_reset_done(struct mlx5_core_dev *dev); void mlx5_sync_reset_unload_flow(struct mlx5_core_dev *dev, bool locked); From e35d7da8dd9e55b37c3e8ab548f6793af0c2ab49 Mon Sep 17 00:00:00 2001 From: Jianbo Liu Date: Tue, 9 Dec 2025 14:56:14 +0200 Subject: [PATCH 088/214] net/mlx5e: Use ip6_dst_lookup instead of ipv6_dst_lookup_flow for MAC init Replace ipv6_stub->ipv6_dst_lookup_flow() with ip6_dst_lookup() in mlx5e_ipsec_init_macs() since IPsec transformations are not needed during Security Association setup - only basic routing information is required for nexthop MAC address resolution. This resolves an issue where XfrmOutNoStates error counter would be incremented when xfrm policy is configured before xfrm state, as the IPsec-aware routing function would attempt policy checks during SA initialization. Fixes: 71670f766b8f ("net/mlx5e: Support routed networks during IPsec MACs initialization") Signed-off-by: Jianbo Liu Reviewed-by: Leon Romanovsky Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-7-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 35d9530037a65..6c79b9cea2efb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -342,9 +342,8 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry, rt_dst_entry = &rt->dst; break; case AF_INET6: - rt_dst_entry = ipv6_stub->ipv6_dst_lookup_flow( - dev_net(netdev), NULL, &fl6, NULL); - if (IS_ERR(rt_dst_entry)) + if (!IS_ENABLED(CONFIG_IPV6) || + ip6_dst_lookup(dev_net(netdev), NULL, &rt_dst_entry, &fl6)) goto neigh; break; default: From 9ab89bde13e5251e1d0507e1cc426edcdfe19142 Mon Sep 17 00:00:00 2001 From: Jianbo Liu Date: Tue, 9 Dec 2025 14:56:15 +0200 Subject: [PATCH 089/214] net/mlx5e: Trigger neighbor resolution for unresolved destinations When initializing the MAC addresses for an outbound IPsec packet offload rule in mlx5e_ipsec_init_macs, the call to dst_neigh_lookup is used to find the next-hop neighbor (typically the gateway in tunnel mode). This call might create a new neighbor entry if one doesn't already exist. This newly created entry starts in the INCOMPLETE state, as the kernel hasn't yet sent an ARP or NDISC probe to resolve the MAC address. In this case, neigh_ha_snapshot will correctly return an all-zero MAC address. IPsec packet offload requires the actual next-hop MAC address to program the rule correctly. If the neighbor state is INCOMPLETE when the rule is created, the hardware rule is programmed with an all-zero destination MAC address. Packets sent using this rule will be subsequently dropped by the receiving network infrastructure or host. This patch adds a check specifically for the outbound offload path. If neigh_ha_snapshot returns an all-zero MAC address, it proactively calls neigh_event_send(n, NULL). This ensures the kernel immediately sends the initial ARP or NDISC probe if one isn't already pending, accelerating the resolution process. This helps prevent the hardware rule from being programmed with an invalid MAC address and avoids packet drops due to unresolved neighbors. Fixes: 71670f766b8f ("net/mlx5e: Support routed networks during IPsec MACs initialization") Signed-off-by: Jianbo Liu Reviewed-by: Leon Romanovsky Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-8-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 6c79b9cea2efb..a8fb4bec369cf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -358,6 +358,9 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry, neigh_ha_snapshot(addr, n, netdev); ether_addr_copy(dst, addr); + if (attrs->dir == XFRM_DEV_OFFLOAD_OUT && + is_zero_ether_addr(addr)) + neigh_event_send(n, NULL); dst_release(rt_dst_entry); neigh_release(n); return; From c8591decd9dbf395cb8ae398e70b0438fdd24aee Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Tue, 9 Dec 2025 14:56:16 +0200 Subject: [PATCH 090/214] net/mlx5e: Do not update BQL of old txqs during channel reconfiguration During channel reconfiguration (e.g., ethtool private flags changes), the driver can trigger a kernel BUG_ON in dql_completed() with the error "kernel BUG at lib/dynamic_queue_limits.c:99". The issue occurs in the following sequence: During mlx5e_safe_switch_params(), old channels are deactivated via mlx5e_deactivate_txqsq(). New channels are created and activated, taking ownership of the netdev_queues and their BQL state. When old channels are closed via mlx5e_close_txqsq(), there may be pending TX descriptors (sq->cc != sq->pc) that were in-flight during the deactivation. mlx5e_free_txqsq_descs() frees these pending descriptors and attempts to complete them via netdev_tx_completed_queue(). However, the BQL state (dql->num_queued and dql->num_completed) have been reset in mlx5e_activate_txqsq and belong to the new queue owner, leading to dql->num_queued - dql->num_completed < nbytes. This triggers BUG_ON(count > num_queued - num_completed) in dql_completed(). Fixes: 3b88a535a8e1 ("net/mlx5e: Defer channels closure to reduce interface down time") Signed-off-by: Tariq Toukan Signed-off-by: William Tu Reviewed-by: Dragos Tatulea Link: https://patch.msgid.link/1765284977-1363052-9-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 14884b9ea7f39..a01ee656a1e7f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -939,7 +939,11 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) sq->dma_fifo_cc = dma_fifo_cc; sq->cc = sqcc; - netdev_tx_completed_queue(sq->txq, npkts, nbytes); + /* Do not update BQL for TXQs that got replaced by new active ones, as + * netdev_tx_reset_queue() is called for them in mlx5e_activate_txqsq(). + */ + if (sq == sq->priv->txq2sq[sq->txq_ix]) + netdev_tx_completed_queue(sq->txq, npkts, nbytes); } #ifdef CONFIG_MLX5_CORE_IPOIB From 4198a14c8c6252fd1191afaa742dd515dcaf3487 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 9 Dec 2025 14:56:17 +0200 Subject: [PATCH 091/214] net/mlx5e: Don't include PSP in the hard MTU calculations Commit [1] added the 40 bytes required by the PSP header+trailer and the UDP header to MLX5E_ETH_HARD_MTU, which limits the device-wide max software MTU that could be set. This is not okay, because most packets are not PSP packets and it doesn't make sense to always reserve space for headers which won't get added in most cases. As it turns out, for TCP connections, PSP overhead is already taken into account in the TCP MSS calculations via inet_csk(sk)->icsk_ext_hdr_len. This was added in commit [2]. This means that the extra space reserved in the hard MTU for mlx5 ends up unused and wasted. Remove the unnecessary 40 byte reservation from hard MTU. [1] commit e5a1861a298e ("net/mlx5e: Implement PSP Tx data path") [2] commit e97269257fe4 ("net: psp: update the TCP MSS to reflect PSP packet overhead") Fixes: e5a1861a298e ("net/mlx5e: Implement PSP Tx data path") Signed-off-by: Cosmin Ratiu Reviewed-by: Shahar Shitrit Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1765284977-1363052-10-git-send-email-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 811178d8976cf..262dc032e276a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -69,7 +69,7 @@ struct page_pool; #define MLX5E_METADATA_ETHER_TYPE (0x8CE4) #define MLX5E_METADATA_ETHER_LEN 8 -#define MLX5E_ETH_HARD_MTU (ETH_HLEN + PSP_ENCAP_HLEN + PSP_TRL_SIZE + VLAN_HLEN + ETH_FCS_LEN) +#define MLX5E_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) #define MLX5E_HW2SW_MTU(params, hwmtu) ((hwmtu) - ((params)->hard_mtu)) #define MLX5E_SW2HW_MTU(params, swmtu) ((swmtu) + ((params)->hard_mtu)) From 15564bd67e2975002f2a8e9defee33e321d3183f Mon Sep 17 00:00:00 2001 From: Scott Mayhew Date: Tue, 9 Dec 2025 14:30:15 -0500 Subject: [PATCH 092/214] net/handshake: duplicate handshake cancellations leak socket When a handshake request is cancelled it is removed from the handshake_net->hn_requests list, but it is still present in the handshake_rhashtbl until it is destroyed. If a second cancellation request arrives for the same handshake request, then remove_pending() will return false... and assuming HANDSHAKE_F_REQ_COMPLETED isn't set in req->hr_flags, we'll continue processing through the out_true label, where we put another reference on the sock and a refcount underflow occurs. This can happen for example if a handshake times out - particularly if the SUNRPC client sends the AUTH_TLS probe to the server but doesn't follow it up with the ClientHello due to a problem with tlshd. When the timeout is hit on the server, the server will send a FIN, which triggers a cancellation request via xs_reset_transport(). When the timeout is hit on the client, another cancellation request happens via xs_tls_handshake_sync(). Add a test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED) in the pending cancel path so duplicate cancels can be detected. Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") Suggested-by: Chuck Lever Signed-off-by: Scott Mayhew Reviewed-by: Chuck Lever Link: https://patch.msgid.link/20251209193015.3032058-1-smayhew@redhat.com Signed-off-by: Paolo Abeni --- net/handshake/request.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/handshake/request.c b/net/handshake/request.c index 89435ed755cd0..6b7e3e0bf3996 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -326,7 +326,11 @@ bool handshake_req_cancel(struct sock *sk) hn = handshake_pernet(net); if (hn && remove_pending(hn, req)) { - /* Request hadn't been accepted */ + /* Request hadn't been accepted - mark cancelled */ + if (test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { + trace_handshake_cancel_busy(net, req, sk); + return false; + } goto out_true; } if (test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { From b98f06f9a5d3b32cf1b3998b4115fb3b5478752d Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 10 Dec 2025 08:11:12 +0000 Subject: [PATCH 093/214] sctp: Fetch inet6_sk() after setting ->pinet6 in sctp_clone_sock(). syzbot reported the lockdep splat below. [0] sctp_clone_sock() sets the child socket's ipv6_mc_list to NULL, but somehow sock_release() in an error path finally acquires lock_sock() in ipv6_sock_mc_close(). The root cause is that sctp_clone_sock() fetches inet6_sk(newsk) before setting newinet->pinet6, meaning that the parent's ipv6_mc_list was actually cleared. Also, sctp_v6_copy_ip_options() uses inet6_sk() but is called before newinet->pinet6 is set. Let's use inet6_sk() only after setting newinet->pinet6. [0]: WARNING: possible recursive locking detected syzkaller #0 Not tainted syz.0.17/5996 is trying to acquire lock: ffff888031af4c60 (sk_lock-AF_INET6){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1700 [inline] ffff888031af4c60 (sk_lock-AF_INET6){+.+.}-{0:0}, at: ipv6_sock_mc_close+0xd3/0x140 net/ipv6/mcast.c:348 but task is already holding lock: ffff888031af4320 (sk_lock-AF_INET6){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1700 [inline] ffff888031af4320 (sk_lock-AF_INET6){+.+.}-{0:0}, at: sctp_getsockopt+0x135/0xb60 net/sctp/socket.c:8131 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(sk_lock-AF_INET6); lock(sk_lock-AF_INET6); *** DEADLOCK *** May be due to missing lock nesting notation 1 lock held by syz.0.17/5996: #0: ffff888031af4320 (sk_lock-AF_INET6){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1700 [inline] #0: ffff888031af4320 (sk_lock-AF_INET6){+.+.}-{0:0}, at: sctp_getsockopt+0x135/0xb60 net/sctp/socket.c:8131 stack backtrace: CPU: 0 UID: 0 PID: 5996 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 Call Trace: dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120 print_deadlock_bug+0x279/0x290 kernel/locking/lockdep.c:3041 check_deadlock kernel/locking/lockdep.c:3093 [inline] validate_chain kernel/locking/lockdep.c:3895 [inline] __lock_acquire+0x2540/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x117/0x340 kernel/locking/lockdep.c:5868 lock_sock_nested+0x48/0x100 net/core/sock.c:3780 lock_sock include/net/sock.h:1700 [inline] ipv6_sock_mc_close+0xd3/0x140 net/ipv6/mcast.c:348 inet6_release+0x47/0x70 net/ipv6/af_inet6.c:482 __sock_release net/socket.c:653 [inline] sock_release+0x85/0x150 net/socket.c:681 sctp_getsockopt_peeloff_common+0x56b/0x770 net/sctp/socket.c:5732 sctp_getsockopt_peeloff_flags+0x13b/0x230 net/sctp/socket.c:5801 sctp_getsockopt+0x3ab/0xb60 net/sctp/socket.c:8151 do_sock_getsockopt+0x2b4/0x3d0 net/socket.c:2399 __sys_getsockopt net/socket.c:2428 [inline] __do_sys_getsockopt net/socket.c:2435 [inline] __se_sys_getsockopt net/socket.c:2432 [inline] __x64_sys_getsockopt+0x1a5/0x250 net/socket.c:2432 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f8f8c38f749 Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 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 a8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffcfdade018 EFLAGS: 00000246 ORIG_RAX: 0000000000000037 RAX: ffffffffffffffda RBX: 00007f8f8c5e5fa0 RCX: 00007f8f8c38f749 RDX: 000000000000007a RSI: 0000000000000084 RDI: 0000000000000003 RBP: 00007f8f8c413f91 R08: 0000200000000040 R09: 0000000000000000 R10: 0000200000000340 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f8f8c5e5fa0 R14: 00007f8f8c5e5fa0 R15: 0000000000000005 Fixes: 16942cf4d3e31 ("sctp: Use sk_clone() in sctp_accept().") Reported-by: syzbot+c59e6bb54e7620495725@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6936d112.a70a0220.38f243.00a7.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20251210081206.1141086-2-kuniyu@google.com Acked-by: Xin Long Signed-off-by: Paolo Abeni --- net/sctp/socket.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d808096f5ab17..2493a5b1fa3ca 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4863,8 +4863,6 @@ static struct sock *sctp_clone_sock(struct sock *sk, newsp->pf->to_sk_daddr(&asoc->peer.primary_addr, newsk); newinet->inet_dport = htons(asoc->peer.port); - - newsp->pf->copy_ip_options(sk, newsk); atomic_set(&newinet->inet_id, get_random_u16()); inet_set_bit(MC_LOOP, newsk); @@ -4874,17 +4872,20 @@ static struct sock *sctp_clone_sock(struct sock *sk, #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) { - struct ipv6_pinfo *newnp = inet6_sk(newsk); + struct ipv6_pinfo *newnp; newinet->pinet6 = &((struct sctp6_sock *)newsk)->inet6; newinet->ipv6_fl_list = NULL; + newnp = inet6_sk(newsk); memcpy(newnp, inet6_sk(sk), sizeof(struct ipv6_pinfo)); newnp->ipv6_mc_list = NULL; newnp->ipv6_ac_list = NULL; } #endif + newsp->pf->copy_ip_options(sk, newsk); + newsp->do_auto_asconf = 0; skb_queue_head_init(&newsp->pd_lobby); From d7ff61e6f3ef856da82bb90cbd0391839a8917a4 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 10 Dec 2025 08:11:13 +0000 Subject: [PATCH 094/214] sctp: Clear inet_opt in sctp_v6_copy_ip_options(). syzbot reported the splat below. [0] Since the cited commit, the child socket inherits all fields of its parent socket unless explicitly cleared. syzbot set IP_OPTIONS to AF_INET6 socket and created a child socket inheriting inet_sk(sk)->inet_opt. sctp_v6_copy_ip_options() only clones np->opt, and leaving inet_opt results in double-free. Let's clear inet_opt in sctp_v6_copy_ip_options(). [0]: BUG: KASAN: double-free in inet_sock_destruct+0x538/0x740 net/ipv4/af_inet.c:159 Free of addr ffff8880304b6d40 by task ksoftirqd/0/15 CPU: 0 UID: 0 PID: 15 Comm: ksoftirqd/0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/02/2025 Call Trace: dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xca/0x240 mm/kasan/report.c:482 kasan_report_invalid_free+0xea/0x110 mm/kasan/report.c:557 check_slab_allocation+0xe1/0x130 include/linux/page-flags.h:-1 kasan_slab_pre_free include/linux/kasan.h:198 [inline] slab_free_hook mm/slub.c:2484 [inline] slab_free mm/slub.c:6630 [inline] kfree+0x148/0x6d0 mm/slub.c:6837 inet_sock_destruct+0x538/0x740 net/ipv4/af_inet.c:159 __sk_destruct+0x89/0x660 net/core/sock.c:2350 sock_put include/net/sock.h:1991 [inline] sctp_endpoint_destroy_rcu+0xa1/0xf0 net/sctp/endpointola.c:197 rcu_do_batch kernel/rcu/tree.c:2605 [inline] rcu_core+0xcab/0x1770 kernel/rcu/tree.c:2861 handle_softirqs+0x286/0x870 kernel/softirq.c:622 run_ksoftirqd+0x9b/0x100 kernel/softirq.c:1063 smpboot_thread_fn+0x542/0xa60 kernel/smpboot.c:160 kthread+0x711/0x8a0 kernel/kthread.c:463 ret_from_fork+0x4bc/0x870 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Allocated by task 6003: kasan_save_stack mm/kasan/common.c:56 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:77 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:417 kasan_kmalloc include/linux/kasan.h:262 [inline] __do_kmalloc_node mm/slub.c:5642 [inline] __kmalloc_noprof+0x411/0x7f0 mm/slub.c:5654 kmalloc_noprof include/linux/slab.h:961 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] ip_options_get+0x51/0x4c0 net/ipv4/ip_options.c:517 do_ip_setsockopt+0x1d9b/0x2d00 net/ipv4/ip_sockglue.c:1087 ip_setsockopt+0x66/0x110 net/ipv4/ip_sockglue.c:1417 do_sock_setsockopt+0x17c/0x1b0 net/socket.c:2360 __sys_setsockopt net/socket.c:2385 [inline] __do_sys_setsockopt net/socket.c:2391 [inline] __se_sys_setsockopt net/socket.c:2388 [inline] __x64_sys_setsockopt+0x13f/0x1b0 net/socket.c:2388 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xfa/0xfa0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 15: kasan_save_stack mm/kasan/common.c:56 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:77 __kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:587 kasan_save_free_info mm/kasan/kasan.h:406 [inline] poison_slab_object mm/kasan/common.c:252 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:284 kasan_slab_free include/linux/kasan.h:234 [inline] slab_free_hook mm/slub.c:2539 [inline] slab_free mm/slub.c:6630 [inline] kfree+0x19a/0x6d0 mm/slub.c:6837 inet_sock_destruct+0x538/0x740 net/ipv4/af_inet.c:159 __sk_destruct+0x89/0x660 net/core/sock.c:2350 sock_put include/net/sock.h:1991 [inline] sctp_endpoint_destroy_rcu+0xa1/0xf0 net/sctp/endpointola.c:197 rcu_do_batch kernel/rcu/tree.c:2605 [inline] rcu_core+0xcab/0x1770 kernel/rcu/tree.c:2861 handle_softirqs+0x286/0x870 kernel/softirq.c:622 run_ksoftirqd+0x9b/0x100 kernel/softirq.c:1063 smpboot_thread_fn+0x542/0xa60 kernel/smpboot.c:160 kthread+0x711/0x8a0 kernel/kthread.c:463 ret_from_fork+0x4bc/0x870 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: 16942cf4d3e31 ("sctp: Use sk_clone() in sctp_accept().") Reported-by: syzbot+ec33a1a006ed5abe7309@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6936d112.a70a0220.38f243.00a8.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20251210081206.1141086-3-kuniyu@google.com Acked-by: Xin Long Signed-off-by: Paolo Abeni --- net/sctp/ipv6.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 069b7e45d8bda..531cb0690007a 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -492,6 +492,8 @@ static void sctp_v6_copy_ip_options(struct sock *sk, struct sock *newsk) struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct ipv6_txoptions *opt; + inet_sk(newsk)->inet_opt = NULL; + newnp = inet6_sk(newsk); rcu_read_lock(); From 1d856251a009d64007d71d01c988bead6d3a098c Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 10 Dec 2025 11:22:54 -0500 Subject: [PATCH 095/214] net/sched: act_mirred: fix loop detection Fix a loop scenario of ethx:egress->ethx:egress Example setup to reproduce: tc qdisc add dev ethx root handle 1: drr tc filter add dev ethx parent 1: protocol ip prio 1 matchall \ action mirred egress redirect dev ethx Now ping out of ethx and you get a deadlock: [ 116.892898][ T307] ============================================ [ 116.893182][ T307] WARNING: possible recursive locking detected [ 116.893418][ T307] 6.18.0-rc6-01205-ge05021a829b8-dirty #204 Not tainted [ 116.893682][ T307] -------------------------------------------- [ 116.893926][ T307] ping/307 is trying to acquire lock: [ 116.894133][ T307] ffff88800c122908 (&sch->root_lock_key){+...}-{3:3}, at: __dev_queue_xmit+0x2210/0x3b50 [ 116.894517][ T307] [ 116.894517][ T307] but task is already holding lock: [ 116.894836][ T307] ffff88800c122908 (&sch->root_lock_key){+...}-{3:3}, at: __dev_queue_xmit+0x2210/0x3b50 [ 116.895252][ T307] [ 116.895252][ T307] other info that might help us debug this: [ 116.895608][ T307] Possible unsafe locking scenario: [ 116.895608][ T307] [ 116.895901][ T307] CPU0 [ 116.896057][ T307] ---- [ 116.896200][ T307] lock(&sch->root_lock_key); [ 116.896392][ T307] lock(&sch->root_lock_key); [ 116.896605][ T307] [ 116.896605][ T307] *** DEADLOCK *** [ 116.896605][ T307] [ 116.896864][ T307] May be due to missing lock nesting notation [ 116.896864][ T307] [ 116.897123][ T307] 6 locks held by ping/307: [ 116.897302][ T307] #0: ffff88800b4b0250 (sk_lock-AF_INET){+.+.}-{0:0}, at: raw_sendmsg+0xb20/0x2cf0 [ 116.897808][ T307] #1: ffffffff88c839c0 (rcu_read_lock){....}-{1:3}, at: ip_output+0xa9/0x600 [ 116.898138][ T307] #2: ffffffff88c839c0 (rcu_read_lock){....}-{1:3}, at: ip_finish_output2+0x2c6/0x1ee0 [ 116.898459][ T307] #3: ffffffff88c83960 (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit+0x200/0x3b50 [ 116.898782][ T307] #4: ffff88800c122908 (&sch->root_lock_key){+...}-{3:3}, at: __dev_queue_xmit+0x2210/0x3b50 [ 116.899132][ T307] #5: ffffffff88c83960 (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit+0x200/0x3b50 [ 116.899442][ T307] [ 116.899442][ T307] stack backtrace: [ 116.899667][ T307] CPU: 2 UID: 0 PID: 307 Comm: ping Not tainted 6.18.0-rc6-01205-ge05021a829b8-dirty #204 PREEMPT(voluntary) [ 116.899672][ T307] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 116.899675][ T307] Call Trace: [ 116.899678][ T307] [ 116.899680][ T307] dump_stack_lvl+0x6f/0xb0 [ 116.899688][ T307] print_deadlock_bug.cold+0xc0/0xdc [ 116.899695][ T307] __lock_acquire+0x11f7/0x1be0 [ 116.899704][ T307] lock_acquire+0x162/0x300 [ 116.899707][ T307] ? __dev_queue_xmit+0x2210/0x3b50 [ 116.899713][ T307] ? srso_alias_return_thunk+0x5/0xfbef5 [ 116.899717][ T307] ? stack_trace_save+0x93/0xd0 [ 116.899723][ T307] _raw_spin_lock+0x30/0x40 [ 116.899728][ T307] ? __dev_queue_xmit+0x2210/0x3b50 [ 116.899731][ T307] __dev_queue_xmit+0x2210/0x3b50 Fixes: 178ca30889a1 ("Revert "net/sched: Fix mirred deadlock on device recursion"") Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20251210162255.1057663-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/act_mirred.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index f27b583def78e..91c96cc625bd6 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -281,6 +281,15 @@ static int tcf_mirred_to_dev(struct sk_buff *skb, struct tcf_mirred *m, want_ingress = tcf_mirred_act_wants_ingress(m_eaction); + if (dev == skb->dev && want_ingress == at_ingress) { + pr_notice_once("tc mirred: Loop (%s:%s --> %s:%s)\n", + netdev_name(skb->dev), + at_ingress ? "ingress" : "egress", + netdev_name(dev), + want_ingress ? "ingress" : "egress"); + goto err_cant_do; + } + /* All mirred/redirected skbs should clear previous ct info */ nf_reset_ct(skb_to_send); if (want_ingress && !at_ingress) /* drop dst for egress -> ingress */ From 5cba412d6a005719d52dc72b6d7e5a59af979eaa Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Dec 2025 11:22:55 -0500 Subject: [PATCH 096/214] selftests/tc-testing: Test case exercising potential mirred redirect deadlock Add a test case that reproduces deadlock scenario where the user has a drr qdisc attached to root and has a mirred action that redirects to self on egress Signed-off-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20251210162255.1057663-2-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- .../tc-testing/tc-tests/actions/mirred.json | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json index b73bd255ea36f..da156feabcbff 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json @@ -1052,5 +1052,51 @@ "$TC qdisc del dev $DEV1 ingress_block 21 clsact", "$TC actions flush action mirred" ] + }, + { + "id": "7eba", + "name": "Redirect multiport: dummy egress -> dummy egress (Loop)", + "category": [ + "filter", + "mirred" + ], + "plugins": { + "requires": [ + "nsPlugin" + ] + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY handle 1: root drr", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1" + ], + "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1", + "expExitCode": "1", + "verifyCmd": "$TC -j -s actions get action mirred index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "mirred", + "mirred_action": "redirect", + "direction": "egress", + "index": 1, + "stats": { + "packets": 1, + "overlimits": 1 + }, + "not_in_hw": true + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] } ] From 2939203ffee818f1e5ebd60bbb85a174d63aab9c Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 11 Dec 2025 10:09:19 +0800 Subject: [PATCH 097/214] net: enetc: do not transmit redirected XDP frames when the link is down In the current implementation, the enetc_xdp_xmit() always transmits redirected XDP frames even if the link is down, but the frames cannot be transmitted from TX BD rings when the link is down, so the frames are still kept in the TX BD rings. If the XDP program is uninstalled, users will see the following warning logs. fsl_enetc 0000:00:00.0 eno0: timeout for tx ring #6 clear More worse, the TX BD ring cannot work properly anymore, because the HW PIR and CIR are not equal after the re-initialization of the TX BD ring. At this point, the BDs between CIR and PIR are invalid, which will cause a hardware malfunction. Another reason is that there is internal context in the ring prefetch logic that will retain the state from the first incarnation of the ring and continue prefetching from the stale location when we re-initialize the ring. The internal context is only reset by an FLR. That is to say, for LS1028A ENETC, software cannot set the HW CIR and PIR when initializing the TX BD ring. It does not make sense to transmit redirected XDP frames when the link is down. Add a link status check to prevent transmission in this condition. This fixes part of the issue, but more complex cases remain. For example, the TX BD ring may still contain unsent frames when the link goes down. Those situations require additional patches, which will build on this one. Fixes: 9d2b68cc108d ("net: enetc: add support for XDP_REDIRECT") Signed-off-by: Wei Fang Reviewed-by: Frank Li Reviewed-by: Hariprasad Kelam Reviewed-by: Vladimir Oltean Link: https://patch.msgid.link/20251211020919.121113-1-wei.fang@nxp.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/enetc/enetc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index d5e5800b84eff..53b26cece16a8 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1787,7 +1787,8 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, int xdp_tx_bd_cnt, i, k; int xdp_tx_frm_cnt = 0; - if (unlikely(test_bit(ENETC_TX_DOWN, &priv->flags))) + if (unlikely(test_bit(ENETC_TX_DOWN, &priv->flags) || + !netif_carrier_ok(ndev))) return -ENETDOWN; enetc_lock_mdio(); From c2a16269742e176fccdd0ef9c016a233491a49ad Mon Sep 17 00:00:00 2001 From: Jian Shen Date: Thu, 11 Dec 2025 10:37:35 +0800 Subject: [PATCH 098/214] net: hns3: using the num_tqps in the vf driver to apply for resources Currently, hdev->htqp is allocated using hdev->num_tqps, and kinfo->tqp is allocated using kinfo->num_tqps. However, kinfo->num_tqps is set to min(new_tqps, hdev->num_tqps); Therefore, kinfo->num_tqps may be smaller than hdev->num_tqps, which causes some hdev->htqp[i] to remain uninitialized in hclgevf_knic_setup(). Thus, this patch allocates hdev->htqp and kinfo->tqp using hdev->num_tqps, ensuring that the lengths of hdev->htqp and kinfo->tqp are consistent and that all elements are properly initialized. Fixes: e2cb1dec9779 ("net: hns3: Add HNS3 VF HCL(Hardware Compatibility Layer) Support") Signed-off-by: Jian Shen Signed-off-by: Jijie Shao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251211023737.2327018-2-shaojijie@huawei.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 8fcf220a120d2..70327a73dee32 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -368,12 +368,12 @@ static int hclgevf_knic_setup(struct hclgevf_dev *hdev) new_tqps = kinfo->rss_size * num_tc; kinfo->num_tqps = min(new_tqps, hdev->num_tqps); - kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, kinfo->num_tqps, + kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps, sizeof(struct hnae3_queue *), GFP_KERNEL); if (!kinfo->tqp) return -ENOMEM; - for (i = 0; i < kinfo->num_tqps; i++) { + for (i = 0; i < hdev->num_tqps; i++) { hdev->htqp[i].q.handle = &hdev->nic; hdev->htqp[i].q.tqp_index = i; kinfo->tqp[i] = &hdev->htqp[i].q; From d180c11aa8a6fa735f9ac2c72c61364a9afc2ba7 Mon Sep 17 00:00:00 2001 From: Jian Shen Date: Thu, 11 Dec 2025 10:37:36 +0800 Subject: [PATCH 099/214] net: hns3: using the num_tqps to check whether tqp_index is out of range when vf get ring info from mbx Currently, rss_size = num_tqps / tc_num. If tc_num is 1, then num_tqps equals rss_size. However, if the tc_num is greater than 1, then rss_size will be less than num_tqps, causing the tqp_index check for subsequent TCs using rss_size to always fail. This patch uses the num_tqps to check whether tqp_index is out of range, instead of rss_size. Fixes: 326334aad024 ("net: hns3: add a check for tqp_index in hclge_get_ring_chain_from_mbx()") Signed-off-by: Jian Shen Signed-off-by: Jijie Shao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251211023737.2327018-3-shaojijie@huawei.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index c7ff12a6c0764..b7d4e06a55d40 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -193,10 +193,10 @@ static int hclge_get_ring_chain_from_mbx( return -EINVAL; for (i = 0; i < ring_num; i++) { - if (req->msg.param[i].tqp_index >= vport->nic.kinfo.rss_size) { + if (req->msg.param[i].tqp_index >= vport->nic.kinfo.num_tqps) { dev_err(&hdev->pdev->dev, "tqp index(%u) is out of range(0-%u)\n", req->msg.param[i].tqp_index, - vport->nic.kinfo.rss_size - 1U); + vport->nic.kinfo.num_tqps - 1U); return -EINVAL; } } From 6ef935e65902bfed53980ad2754b06a284ea8ac1 Mon Sep 17 00:00:00 2001 From: Jian Shen Date: Thu, 11 Dec 2025 10:37:37 +0800 Subject: [PATCH 100/214] net: hns3: add VLAN id validation before using Currently, the VLAN id may be used without validation when receive a VLAN configuration mailbox from VF. The length of vlan_del_fail_bmap is BITS_TO_LONGS(VLAN_N_VID). It may cause out-of-bounds memory access once the VLAN id is bigger than or equal to VLAN_N_VID. Therefore, VLAN id needs to be checked to ensure it is within the range of VLAN_N_VID. Fixes: fe4144d47eef ("net: hns3: sync VLAN filter entries when kill VLAN ID failed") Signed-off-by: Jian Shen Signed-off-by: Jijie Shao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251211023737.2327018-4-shaojijie@huawei.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index cf8abbe018402..c589baea7c775 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -10555,6 +10555,9 @@ int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto, bool writen_to_tbl = false; int ret = 0; + if (vlan_id >= VLAN_N_VID) + return -EINVAL; + /* When device is resetting or reset failed, firmware is unable to * handle mailbox. Just record the vlan id, and remove it after * reset finished. From 348240e5fa901d3d4ba8dffa0e2ba9fc7aba93ab Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Thu, 4 Dec 2025 22:40:20 +0200 Subject: [PATCH 101/214] Bluetooth: MGMT: report BIS capability flags in supported settings MGMT_SETTING_ISO_BROADCASTER and MGMT_SETTING_ISO_RECEIVER flags are missing from supported_settings although they are in current_settings. Report them also in supported_settings to be consistent. Fixes: ae7533613133 ("Bluetooth: Check for ISO support in controller") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c11cdef42b6f6..5be9b8c919490 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -849,6 +849,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (cis_peripheral_capable(hdev)) settings |= MGMT_SETTING_CIS_PERIPHERAL; + if (bis_capable(hdev)) + settings |= MGMT_SETTING_ISO_BROADCASTER; + + if (sync_recv_capable(hdev)) + settings |= MGMT_SETTING_ISO_SYNC_RECEIVER; + if (ll_privacy_capable(hdev)) settings |= MGMT_SETTING_LL_PRIVACY; From 252714f1e8bdd542025b16321c790458014d6880 Mon Sep 17 00:00:00 2001 From: Raphael Pinsonneault-Thibeault Date: Wed, 10 Dec 2025 11:02:28 -0500 Subject: [PATCH 102/214] Bluetooth: btusb: revert use of devm_kzalloc in btusb This reverts commit 98921dbd00c4e ("Bluetooth: Use devm_kzalloc in btusb.c file"). In btusb_probe(), we use devm_kzalloc() to allocate the btusb data. This ties the lifetime of all the btusb data to the binding of a driver to one interface, INTF. In a driver that binds to other interfaces, ISOC and DIAG, this is an accident waiting to happen. The issue is revealed in btusb_disconnect(), where calling usb_driver_release_interface(&btusb_driver, data->intf) will have devm free the data that is also being used by the other interfaces of the driver that may not be released yet. To fix this, revert the use of devm and go back to freeing memory explicitly. Fixes: 98921dbd00c4e ("Bluetooth: Use devm_kzalloc in btusb.c file") Signed-off-by: Raphael Pinsonneault-Thibeault Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8ed3883ab8eef..ded09e94d296d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4052,7 +4052,7 @@ static int btusb_probe(struct usb_interface *intf, return -ENODEV; } - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -4075,8 +4075,10 @@ static int btusb_probe(struct usb_interface *intf, } } - if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + kfree(data); return -ENODEV; + } if (id->driver_info & BTUSB_AMP) { data->cmdreq_type = USB_TYPE_CLASS | 0x01; @@ -4131,8 +4133,10 @@ static int btusb_probe(struct usb_interface *intf, data->recv_acl = hci_recv_frame; hdev = hci_alloc_dev_priv(priv_size); - if (!hdev) + if (!hdev) { + kfree(data); return -ENOMEM; + } hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); @@ -4406,6 +4410,7 @@ static int btusb_probe(struct usb_interface *intf, if (data->reset_gpio) gpiod_put(data->reset_gpio); hci_free_dev(hdev); + kfree(data); return err; } @@ -4454,6 +4459,7 @@ static void btusb_disconnect(struct usb_interface *intf) } hci_free_dev(hdev); + kfree(data); } #ifdef CONFIG_PM From bf4172bd870c3a34d3065cbb39192c22cbd7b18d Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Dec 2025 22:24:51 -0800 Subject: [PATCH 103/214] net: usb: sr9700: support devices with virtual driver CD Some SR9700 devices have an SPI flash chip containing a virtual driver CD, in which case they appear as a device with two interfaces and product ID 0x9702. Interface 0 is the driver CD and interface 1 is the Ethernet device. Link: https://github.com/name-kurniawan/usb-lan Link: https://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=2185 Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20251211062451.139036-1-enelsonmoore@gmail.com [pabeni@redhat.com: fixes link tags] Signed-off-by: Paolo Abeni --- drivers/net/usb/sr9700.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index 091bc2aca7e8e..d8ffb59eaf348 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -539,6 +539,11 @@ static const struct usb_device_id products[] = { USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ .driver_info = (unsigned long)&sr9700_driver_info, }, + { + /* SR9700 with virtual driver CD-ROM - interface 0 is the CD-ROM device */ + USB_DEVICE_INTERFACE_NUMBER(0x0fe6, 0x9702, 1), + .driver_info = (unsigned long)&sr9700_driver_info, + }, {}, /* END */ }; From d1a1a4bade4b20c0858d0b2f81d2611de055f675 Mon Sep 17 00:00:00 2001 From: Jacky Chou Date: Thu, 11 Dec 2025 14:24:58 +0800 Subject: [PATCH 104/214] net: mdio: aspeed: add dummy read to avoid read-after-write issue The Aspeed MDIO controller may return incorrect data when a read operation follows immediately after a write. Due to a controller bug, the subsequent read can latch stale data, causing the polling logic to terminate earlier than expected. To work around this hardware issue, insert a dummy read after each write operation. This ensures that the next actual read returns the correct data and prevents premature polling exit. This workaround has been verified to stabilize MDIO transactions on affected Aspeed platforms. Fixes: f160e99462c6 ("net: phy: Add mdio-aspeed") Signed-off-by: Jacky Chou Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20251211-aspeed_mdio_add_dummy_read-v3-1-382868869004@aspeedtech.com Signed-off-by: Paolo Abeni --- drivers/net/mdio/mdio-aspeed.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/mdio/mdio-aspeed.c b/drivers/net/mdio/mdio-aspeed.c index e55be6dc9ae70..d6b9004c61dc1 100644 --- a/drivers/net/mdio/mdio-aspeed.c +++ b/drivers/net/mdio/mdio-aspeed.c @@ -63,6 +63,13 @@ static int aspeed_mdio_op(struct mii_bus *bus, u8 st, u8 op, u8 phyad, u8 regad, iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL); + /* Workaround for read-after-write issue. + * The controller may return stale data if a read follows immediately + * after a write. A dummy read forces the hardware to update its + * internal state, ensuring that the next real read returns correct data. + */ + ioread32(ctx->base + ASPEED_MDIO_CTRL); + return readl_poll_timeout(ctx->base + ASPEED_MDIO_CTRL, ctrl, !(ctrl & ASPEED_MDIO_CTRL_FIRE), ASPEED_MDIO_INTERVAL_US, From 15ef641a0c6728d25a400df73922e80ab2cf029c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Thu, 11 Dec 2025 15:37:56 +0800 Subject: [PATCH 105/214] fjes: Add missing iounmap in fjes_hw_init() In error paths, add fjes_hw_iounmap() to release the resource acquired by fjes_hw_iomap(). Add a goto label to do so. Fixes: 8cdc3f6c5d22 ("fjes: Hardware initialization routine") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Signed-off-by: Simon Horman Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251211073756.101824-1-lihaoxiang@isrc.iscas.ac.cn Signed-off-by: Paolo Abeni --- drivers/net/fjes/fjes_hw.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index b9b5554ea8620..5ad2673f213d6 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -334,7 +334,7 @@ int fjes_hw_init(struct fjes_hw *hw) ret = fjes_hw_reset(hw); if (ret) - return ret; + goto err_iounmap; fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, true); @@ -347,8 +347,10 @@ int fjes_hw_init(struct fjes_hw *hw) hw->max_epid = fjes_hw_get_max_epid(hw); hw->my_epid = fjes_hw_get_my_epid(hw); - if ((hw->max_epid == 0) || (hw->my_epid >= hw->max_epid)) - return -ENXIO; + if ((hw->max_epid == 0) || (hw->my_epid >= hw->max_epid)) { + ret = -ENXIO; + goto err_iounmap; + } ret = fjes_hw_setup(hw); @@ -356,6 +358,10 @@ int fjes_hw_init(struct fjes_hw *hw) hw->hw_info.trace_size = FJES_DEBUG_BUFFER_SIZE; return ret; + +err_iounmap: + fjes_hw_iounmap(hw); + return ret; } void fjes_hw_exit(struct fjes_hw *hw) From 1e5a541420b8c6d87d88eb50b6b978cdeafee1c9 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Thu, 11 Dec 2025 12:13:13 +0400 Subject: [PATCH 106/214] net: phy: mediatek: fix nvmem cell reference leak in mt798x_phy_calibration When nvmem_cell_read() fails in mt798x_phy_calibration(), the function returns without calling nvmem_cell_put(), leaking the cell reference. Move nvmem_cell_put() right after nvmem_cell_read() to ensure the cell reference is always released regardless of the read result. Found via static analysis and code review. Fixes: 98c485eaf509 ("net: phy: add driver for MediaTek SoC built-in GE PHYs") Cc: stable@vger.kernel.org Signed-off-by: Miaoqian Lin Reviewed-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20251211081313.2368460-1-linmq006@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/phy/mediatek/mtk-ge-soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/mediatek/mtk-ge-soc.c b/drivers/net/phy/mediatek/mtk-ge-soc.c index cd09fbf92ef23..2c4bbc236202b 100644 --- a/drivers/net/phy/mediatek/mtk-ge-soc.c +++ b/drivers/net/phy/mediatek/mtk-ge-soc.c @@ -1167,9 +1167,9 @@ static int mt798x_phy_calibration(struct phy_device *phydev) } buf = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); if (IS_ERR(buf)) return PTR_ERR(buf); - nvmem_cell_put(cell); if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { phydev_err(phydev, "invalid efuse data\n"); From 5498227676303e3ffa9a3a46214af96bc3e81314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 11 Dec 2025 12:50:05 +0100 Subject: [PATCH 107/214] net: openvswitch: Avoid needlessly taking the RTNL on vport destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The openvswitch teardown code will immediately call ovs_netdev_detach_dev() in response to a NETDEV_UNREGISTER notification. It will then start the dp_notify_work workqueue, which will later end up calling the vport destroy() callback. This callback takes the RTNL to do another ovs_netdev_detach_port(), which in this case is unnecessary. This causes extra pressure on the RTNL, in some cases leading to "unregister_netdevice: waiting for XX to become free" warnings on teardown. We can straight-forwardly avoid the extra RTNL lock acquisition by checking the device flags before taking the lock, and skip the locking altogether if the IFF_OVS_DATAPATH flag has already been unset. Fixes: b07c26511e94 ("openvswitch: fix vport-netdev unregister") Tested-by: Adrian Moreno Signed-off-by: Toke Høiland-Jørgensen Acked-by: Eelco Chaudron Acked-by: Aaron Conole Link: https://patch.msgid.link/20251211115006.228876-1-toke@redhat.com Signed-off-by: Paolo Abeni --- net/openvswitch/vport-netdev.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 91a11067e4588..6574f9bcdc026 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -160,10 +160,19 @@ void ovs_netdev_detach_dev(struct vport *vport) static void netdev_destroy(struct vport *vport) { - rtnl_lock(); - if (netif_is_ovs_port(vport->dev)) - ovs_netdev_detach_dev(vport); - rtnl_unlock(); + /* When called from ovs_db_notify_wq() after a dp_device_event(), the + * port has already been detached, so we can avoid taking the RTNL by + * checking this first. + */ + if (netif_is_ovs_port(vport->dev)) { + rtnl_lock(); + /* Check again while holding the lock to ensure we don't race + * with the netdev notifier and detach twice. + */ + if (netif_is_ovs_port(vport->dev)) + ovs_netdev_detach_dev(vport); + rtnl_unlock(); + } call_rcu(&vport->rcu, vport_netdev_free); } From db5b4e39c4e63700c68a7e65fc4e1f1375273476 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 Dec 2025 17:35:50 +0000 Subject: [PATCH 108/214] ip6_gre: make ip6gre_header() robust Over the years, syzbot found many ways to crash the kernel in ip6gre_header() [1]. This involves team or bonding drivers ability to dynamically change their dev->needed_headroom and/or dev->hard_header_len In this particular crash mld_newpack() allocated an skb with a too small reserve/headroom, and by the time mld_sendpack() was called, syzbot managed to attach an ip6gre device. [1] skbuff: skb_under_panic: text:ffffffff8a1d69a8 len:136 put:40 head:ffff888059bc7000 data:ffff888059bc6fe8 tail:0x70 end:0x6c0 dev:team0 ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:213 ! skb_under_panic net/core/skbuff.c:223 [inline] skb_push+0xc3/0xe0 net/core/skbuff.c:2641 ip6gre_header+0xc8/0x790 net/ipv6/ip6_gre.c:1371 dev_hard_header include/linux/netdevice.h:3436 [inline] neigh_connected_output+0x286/0x460 net/core/neighbour.c:1618 neigh_output include/net/neighbour.h:556 [inline] ip6_finish_output2+0xfb3/0x1480 net/ipv6/ip6_output.c:136 __ip6_finish_output net/ipv6/ip6_output.c:-1 [inline] ip6_finish_output+0x234/0x7d0 net/ipv6/ip6_output.c:220 NF_HOOK_COND include/linux/netfilter.h:307 [inline] ip6_output+0x340/0x550 net/ipv6/ip6_output.c:247 NF_HOOK+0x9e/0x380 include/linux/netfilter.h:318 mld_sendpack+0x8d4/0xe60 net/ipv6/mcast.c:1855 mld_send_cr net/ipv6/mcast.c:2154 [inline] mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 Fixes: c12b395a4664 ("gre: Support GRE over IPv6") Reported-by: syzbot+43a2ebcf2a64b1102d64@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/693b002c.a70a0220.33cd7b.0033.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20251211173550.2032674-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv6/ip6_gre.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index c82a75510c0e2..8bc3f05f594ed 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1366,9 +1366,16 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev, { struct ip6_tnl *t = netdev_priv(dev); struct ipv6hdr *ipv6h; + int needed; __be16 *p; - ipv6h = skb_push(skb, t->hlen + sizeof(*ipv6h)); + needed = t->hlen + sizeof(*ipv6h); + if (skb_headroom(skb) < needed && + pskb_expand_head(skb, HH_DATA_ALIGN(needed - skb_headroom(skb)), + 0, GFP_ATOMIC)) + return -needed; + + ipv6h = skb_push(skb, needed); ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb, t->fl.u.ip6.flowlabel, true, &t->fl.u.ip6)); From dabac51b8102e1643d8e297a8e6948dab190aa4c Mon Sep 17 00:00:00 2001 From: Wang Liang Date: Fri, 12 Dec 2025 09:27:23 +0800 Subject: [PATCH 109/214] net/handshake: Fix null-ptr-deref in handshake_complete() A null pointer dereference in handshake_complete() was observed [1]. When handshake_req_next() return NULL in handshake_nl_accept_doit(), function handshake_complete() will be called unexpectedly which triggers this crash. Fix it by goto out_status when req is NULL. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000005: 0000 [#1] SMP KASAN PTI RIP: 0010:handshake_complete+0x36/0x2b0 net/handshake/request.c:288 Call Trace: handshake_nl_accept_doit+0x32d/0x7e0 net/handshake/netlink.c:129 genl_family_rcv_msg_doit+0x204/0x300 net/netlink/genetlink.c:1115 genl_family_rcv_msg+0x436/0x670 net/netlink/genetlink.c:1195 genl_rcv_msg+0xcc/0x170 net/netlink/genetlink.c:1210 netlink_rcv_skb+0x14c/0x430 net/netlink/af_netlink.c:2550 genl_rcv+0x2d/0x40 net/netlink/genetlink.c:1219 netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline] netlink_unicast+0x878/0xb20 net/netlink/af_netlink.c:1344 netlink_sendmsg+0x897/0xd70 net/netlink/af_netlink.c:1894 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] ____sys_sendmsg+0xa39/0xbf0 net/socket.c:2592 ___sys_sendmsg+0x121/0x1c0 net/socket.c:2646 __sys_sendmsg+0x155/0x200 net/socket.c:2678 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x5f/0x350 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e Fixes: fe67b063f687 ("net/handshake: convert handshake_nl_accept_doit() to FD_PREPARE()") Reviewed-by: Chuck Lever Reported-by: Dan Carpenter Closes: https://lore.kernel.org/kernel-tls-handshake/aScekpuOYHRM9uOd@morisot.1015granger.net/T/#m7cfa5c11efc626d77622b2981591197a2acdd65e Signed-off-by: Wang Liang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251212012723.4111831-1-wangliang74@huawei.com Signed-off-by: Paolo Abeni --- net/handshake/netlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 1d33a4675a483..b989456fc4c5f 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -126,7 +126,8 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) } out_complete: - handshake_complete(req, -EIO, NULL); + if (req) + handshake_complete(req, -EIO, NULL); out_status: trace_handshake_cmd_accept_err(net, req, NULL, err); return err; From 22060d791e1645acb944f5bdfde030e86a00da51 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 12 Dec 2025 11:29:53 +0100 Subject: [PATCH 110/214] team: fix check for port enabled in team_queue_override_port_prio_changed() There has been a syzkaller bug reported recently with the following trace: list_del corruption, ffff888058bea080->prev is LIST_POISON2 (dead000000000122) ------------[ cut here ]------------ kernel BUG at lib/list_debug.c:59! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 3 UID: 0 PID: 21246 Comm: syz.0.2928 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 RIP: 0010:__list_del_entry_valid_or_report+0x13e/0x200 lib/list_debug.c:59 Code: 48 c7 c7 e0 71 f0 8b e8 30 08 ef fc 90 0f 0b 48 89 ef e8 a5 02 55 fd 48 89 ea 48 89 de 48 c7 c7 40 72 f0 8b e8 13 08 ef fc 90 <0f> 0b 48 89 ef e8 88 02 55 fd 48 89 ea 48 b8 00 00 00 00 00 fc ff RSP: 0018:ffffc9000d49f370 EFLAGS: 00010286 RAX: 000000000000004e RBX: ffff888058bea080 RCX: ffffc9002817d000 RDX: 0000000000000000 RSI: ffffffff819becc6 RDI: 0000000000000005 RBP: dead000000000122 R08: 0000000000000005 R09: 0000000000000000 R10: 0000000080000000 R11: 0000000000000001 R12: ffff888039e9c230 R13: ffff888058bea088 R14: ffff888058bea080 R15: ffff888055461480 FS: 00007fbbcfe6f6c0(0000) GS:ffff8880d6d0a000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000110c3afcb0 CR3: 00000000382c7000 CR4: 0000000000352ef0 Call Trace: __list_del_entry_valid include/linux/list.h:132 [inline] __list_del_entry include/linux/list.h:223 [inline] list_del_rcu include/linux/rculist.h:178 [inline] __team_queue_override_port_del drivers/net/team/team_core.c:826 [inline] __team_queue_override_port_del drivers/net/team/team_core.c:821 [inline] team_queue_override_port_prio_changed drivers/net/team/team_core.c:883 [inline] team_priority_option_set+0x171/0x2f0 drivers/net/team/team_core.c:1534 team_option_set drivers/net/team/team_core.c:376 [inline] team_nl_options_set_doit+0x8ae/0xe60 drivers/net/team/team_core.c:2653 genl_family_rcv_msg_doit+0x209/0x2f0 net/netlink/genetlink.c:1115 genl_family_rcv_msg net/netlink/genetlink.c:1195 [inline] genl_rcv_msg+0x55c/0x800 net/netlink/genetlink.c:1210 netlink_rcv_skb+0x158/0x420 net/netlink/af_netlink.c:2552 genl_rcv+0x28/0x40 net/netlink/genetlink.c:1219 netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline] netlink_unicast+0x5aa/0x870 net/netlink/af_netlink.c:1346 netlink_sendmsg+0x8c8/0xdd0 net/netlink/af_netlink.c:1896 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] ____sys_sendmsg+0xa98/0xc70 net/socket.c:2630 ___sys_sendmsg+0x134/0x1d0 net/socket.c:2684 __sys_sendmsg+0x16d/0x220 net/socket.c:2716 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0xfa0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f The problem is in this flow: 1) Port is enabled, queue_id != 0, in qom_list 2) Port gets disabled -> team_port_disable() -> team_queue_override_port_del() -> del (removed from list) 3) Port is disabled, queue_id != 0, not in any list 4) Priority changes -> team_queue_override_port_prio_changed() -> checks: port disabled && queue_id != 0 -> calls del - hits the BUG as it is removed already To fix this, change the check in team_queue_override_port_prio_changed() so it returns early if port is not enabled. Reported-by: syzbot+422806e5f4cce722a71f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=422806e5f4cce722a71f Fixes: 6c31ff366c11 ("team: remove synchronize_rcu() called during queue override change") Signed-off-by: Jiri Pirko Reviewed-by: Simon Horman Signed-off-by: NipaLocal --- drivers/net/team/team_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 4d5c9ae8f2219..c08a5c1bd6e4d 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -878,7 +878,7 @@ static void __team_queue_override_enabled_check(struct team *team) static void team_queue_override_port_prio_changed(struct team *team, struct team_port *port) { - if (!port->queue_id || team_port_enabled(port)) + if (!port->queue_id || !team_port_enabled(port)) return; __team_queue_override_port_del(team, port); __team_queue_override_port_add(team, port); From d18e0adf61b090406b1da92f8b9d7f36cbf348b3 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 12 Dec 2025 13:54:03 +0100 Subject: [PATCH 111/214] mptcp: fallback earlier on simult connection Syzkaller reports a simult-connect race leading to inconsistent fallback status: WARNING: CPU: 3 PID: 33 at net/mptcp/subflow.c:1515 subflow_data_ready+0x40b/0x7c0 net/mptcp/subflow.c:1515 Modules linked in: CPU: 3 UID: 0 PID: 33 Comm: ksoftirqd/3 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 RIP: 0010:subflow_data_ready+0x40b/0x7c0 net/mptcp/subflow.c:1515 Code: 89 ee e8 78 61 3c f6 40 84 ed 75 21 e8 8e 66 3c f6 44 89 fe bf 07 00 00 00 e8 c1 61 3c f6 41 83 ff 07 74 09 e8 76 66 3c f6 90 <0f> 0b 90 e8 6d 66 3c f6 48 89 df e8 e5 ad ff ff 31 ff 89 c5 89 c6 RSP: 0018:ffffc900006cf338 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff888031acd100 RCX: ffffffff8b7f2abf RDX: ffff88801e6ea440 RSI: ffffffff8b7f2aca RDI: 0000000000000005 RBP: 0000000000000000 R08: 0000000000000005 R09: 0000000000000007 R10: 0000000000000004 R11: 0000000000002c10 R12: ffff88802ba69900 R13: 1ffff920000d9e67 R14: ffff888046f81800 R15: 0000000000000004 FS: 0000000000000000(0000) GS:ffff8880d69bc000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000560fc0ca1670 CR3: 0000000032c3a000 CR4: 0000000000352ef0 Call Trace: tcp_data_queue+0x13b0/0x4f90 net/ipv4/tcp_input.c:5197 tcp_rcv_state_process+0xfdf/0x4ec0 net/ipv4/tcp_input.c:6922 tcp_v6_do_rcv+0x492/0x1740 net/ipv6/tcp_ipv6.c:1672 tcp_v6_rcv+0x2976/0x41e0 net/ipv6/tcp_ipv6.c:1918 ip6_protocol_deliver_rcu+0x188/0x1520 net/ipv6/ip6_input.c:438 ip6_input_finish+0x1e4/0x4b0 net/ipv6/ip6_input.c:489 NF_HOOK include/linux/netfilter.h:318 [inline] NF_HOOK include/linux/netfilter.h:312 [inline] ip6_input+0x105/0x2f0 net/ipv6/ip6_input.c:500 dst_input include/net/dst.h:471 [inline] ip6_rcv_finish net/ipv6/ip6_input.c:79 [inline] NF_HOOK include/linux/netfilter.h:318 [inline] NF_HOOK include/linux/netfilter.h:312 [inline] ipv6_rcv+0x264/0x650 net/ipv6/ip6_input.c:311 __netif_receive_skb_one_core+0x12d/0x1e0 net/core/dev.c:5979 __netif_receive_skb+0x1d/0x160 net/core/dev.c:6092 process_backlog+0x442/0x15e0 net/core/dev.c:6444 __napi_poll.constprop.0+0xba/0x550 net/core/dev.c:7494 napi_poll net/core/dev.c:7557 [inline] net_rx_action+0xa9f/0xfe0 net/core/dev.c:7684 handle_softirqs+0x216/0x8e0 kernel/softirq.c:579 run_ksoftirqd kernel/softirq.c:968 [inline] run_ksoftirqd+0x3a/0x60 kernel/softirq.c:960 smpboot_thread_fn+0x3f7/0xae0 kernel/smpboot.c:160 kthread+0x3c2/0x780 kernel/kthread.c:463 ret_from_fork+0x5d7/0x6f0 arch/x86/kernel/process.c:148 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 The TCP subflow can process the simult-connect syn-ack packet after transitioning to TCP_FIN1 state, bypassing the MPTCP fallback check, as the sk_state_change() callback is not invoked for * -> FIN_WAIT1 transitions. That will move the msk socket to an inconsistent status and the next incoming data will hit the reported splat. Close the race moving the simult-fallback check at the earliest possible stage - that is at syn-ack generation time. About the fixes tags: [2] was supposed to also fix this issue introduced by [3]. [1] is required as a dependence: it was not explicitly marked as a fix, but it is one and it has already been backported before [3]. In other words, this commit should be backported up to [3], including [2] and [1] if that's not already there. Fixes: 23e89e8ee7be ("tcp: Don't drop SYN+ACK for simultaneous connect().") [1] Fixes: 4fd19a307016 ("mptcp: fix inconsistent state on fastopen race") [2] Fixes: 1e777f39b4d7 ("mptcp: add MSG_FASTOPEN sendmsg flag support") [3] Cc: stable@vger.kernel.org Reported-by: syzbot+0ff6b771b4f7a5bce83b@syzkaller.appspotmail.com Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/586 Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Signed-off-by: NipaLocal --- net/mptcp/options.c | 10 ++++++++++ net/mptcp/protocol.h | 6 ++---- net/mptcp/subflow.c | 6 ------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index f24ae7d40e883..43df4293f58bf 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -408,6 +408,16 @@ bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb, */ subflow->snd_isn = TCP_SKB_CB(skb)->end_seq; if (subflow->request_mptcp) { + if (unlikely(subflow_simultaneous_connect(sk))) { + WARN_ON_ONCE(!mptcp_try_fallback(sk, MPTCP_MIB_SIMULTCONNFALLBACK)); + + /* Ensure mptcp_finish_connect() will not process the + * MPC handshake. + */ + subflow->request_mptcp = 0; + return false; + } + opts->suboptions = OPTION_MPTCP_MPC_SYN; opts->csum_reqd = mptcp_is_checksum_enabled(sock_net(sk)); opts->allow_join_id0 = mptcp_allow_join_id0(sock_net(sk)); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 9c0d17876b22f..bed0c9aa28b61 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -1337,10 +1337,8 @@ static inline bool subflow_simultaneous_connect(struct sock *sk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); - return (1 << sk->sk_state) & - (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | TCPF_CLOSING) && - is_active_ssk(subflow) && - !subflow->conn_finished; + /* Note that the sk state implies !subflow->conn_finished. */ + return sk->sk_state == TCP_SYN_RECV && is_active_ssk(subflow); } #ifdef CONFIG_SYN_COOKIES diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 86ce58ae533df..96d54cb2cd93f 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1878,12 +1878,6 @@ static void subflow_state_change(struct sock *sk) __subflow_state_change(sk); - if (subflow_simultaneous_connect(sk)) { - WARN_ON_ONCE(!mptcp_try_fallback(sk, MPTCP_MIB_SIMULTCONNFALLBACK)); - subflow->conn_finished = 1; - mptcp_propagate_state(parent, sk, subflow, NULL); - } - /* as recvmsg() does not acquire the subflow socket for ssk selection * a fin packet carrying a DSS can be unnoticed if we don't trigger * the data available machinery here. From bb86c5d4388684757dedbf9e66163d70fb6e9003 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 12 Dec 2025 13:54:04 +0100 Subject: [PATCH 112/214] mptcp: ensure context reset on disconnect() After the blamed commit below, if the MPC subflow is already in TCP_CLOSE status or has fallback to TCP at mptcp_disconnect() time, mptcp_do_fastclose() skips setting the `send_fastclose flag` and the later __mptcp_close_ssk() does not reset anymore the related subflow context. Any later connection will be created with both the `request_mptcp` flag and the msk-level fallback status off (it is unconditionally cleared at MPTCP disconnect time), leading to a warning in subflow_data_ready(): WARNING: CPU: 26 PID: 8996 at net/mptcp/subflow.c:1519 subflow_data_ready (net/mptcp/subflow.c:1519 (discriminator 13)) Modules linked in: CPU: 26 UID: 0 PID: 8996 Comm: syz.22.39 Not tainted 6.18.0-rc7-05427-g11fc074f6c36 #1 PREEMPT(voluntary) Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 RIP: 0010:subflow_data_ready (net/mptcp/subflow.c:1519 (discriminator 13)) Code: 90 0f 0b 90 90 e9 04 fe ff ff e8 b7 1e f5 fe 89 ee bf 07 00 00 00 e8 db 19 f5 fe 83 fd 07 0f 84 35 ff ff ff e8 9d 1e f5 fe 90 <0f> 0b 90 e9 27 ff ff ff e8 8f 1e f5 fe 4c 89 e7 48 89 de e8 14 09 RSP: 0018:ffffc9002646fb30 EFLAGS: 00010293 RAX: 0000000000000000 RBX: ffff88813b218000 RCX: ffffffff825c8435 RDX: ffff8881300b3580 RSI: ffffffff825c8443 RDI: 0000000000000005 RBP: 000000000000000b R08: ffffffff825c8435 R09: 000000000000000b R10: 0000000000000005 R11: 0000000000000007 R12: ffff888131ac0000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 FS: 00007f88330af6c0(0000) GS:ffff888a93dd2000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f88330aefe8 CR3: 000000010ff59000 CR4: 0000000000350ef0 Call Trace: tcp_data_ready (net/ipv4/tcp_input.c:5356) tcp_data_queue (net/ipv4/tcp_input.c:5445) tcp_rcv_state_process (net/ipv4/tcp_input.c:7165) tcp_v4_do_rcv (net/ipv4/tcp_ipv4.c:1955) __release_sock (include/net/sock.h:1158 (discriminator 6) net/core/sock.c:3180 (discriminator 6)) release_sock (net/core/sock.c:3737) mptcp_sendmsg (net/mptcp/protocol.c:1763 net/mptcp/protocol.c:1857) inet_sendmsg (net/ipv4/af_inet.c:853 (discriminator 7)) __sys_sendto (net/socket.c:727 (discriminator 15) net/socket.c:742 (discriminator 15) net/socket.c:2244 (discriminator 15)) __x64_sys_sendto (net/socket.c:2247) do_syscall_64 (arch/x86/entry/syscall_64.c:63 (discriminator 1) arch/x86/entry/syscall_64.c:94 (discriminator 1)) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) RIP: 0033:0x7f883326702d Address the issue setting an explicit `fastclosing` flag at fastclose time, and checking such flag after mptcp_do_fastclose(). Fixes: ae155060247b ("mptcp: fix duplicate reset on fastclose") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Signed-off-by: NipaLocal --- net/mptcp/protocol.c | 8 +++++--- net/mptcp/protocol.h | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 9b1fafd87cb94..f505b780f7139 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2467,10 +2467,10 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) */ static void __mptcp_subflow_disconnect(struct sock *ssk, struct mptcp_subflow_context *subflow, - unsigned int flags) + bool fastclosing) { if (((1 << ssk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || - subflow->send_fastclose) { + fastclosing) { /* The MPTCP code never wait on the subflow sockets, TCP-level * disconnect should never fail */ @@ -2538,7 +2538,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, need_push = (flags & MPTCP_CF_PUSH) && __mptcp_retransmit_pending_data(sk); if (!dispose_it) { - __mptcp_subflow_disconnect(ssk, subflow, flags); + __mptcp_subflow_disconnect(ssk, subflow, msk->fastclosing); release_sock(ssk); goto out; @@ -2884,6 +2884,7 @@ static void mptcp_do_fastclose(struct sock *sk) mptcp_set_state(sk, TCP_CLOSE); mptcp_backlog_purge(sk); + msk->fastclosing = 1; /* Explicitly send the fastclose reset as need */ if (__mptcp_check_fallback(msk)) @@ -3418,6 +3419,7 @@ static int mptcp_disconnect(struct sock *sk, int flags) msk->bytes_sent = 0; msk->bytes_retrans = 0; msk->rcvspace_init = 0; + msk->fastclosing = 0; /* for fallback's sake */ WRITE_ONCE(msk->ack_seq, 0); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index bed0c9aa28b61..66e9735007912 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -320,7 +320,8 @@ struct mptcp_sock { fastopening:1, in_accept_queue:1, free_first:1, - rcvspace_init:1; + rcvspace_init:1, + fastclosing:1; u32 notsent_lowat; int keepalive_cnt; int keepalive_idle; From 9278f92ed26217e7cda9183dc3af797efe01388d Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Sat, 13 Dec 2025 10:13:36 +0000 Subject: [PATCH 113/214] erspan: Initialize options_len before referencing options. The struct ip_tunnel_info has a flexible array member named options that is protected by a counted_by(options_len) attribute. The compiler will use this information to enforce runtime bounds checking deployed by FORTIFY_SOURCE string helpers. As laid out in the GCC documentation, the counter must be initialized before the first reference to the flexible array member. After scanning through the files that use struct ip_tunnel_info and also refer to options or options_len, it appears the normal case is to use the ip_tunnel_info_opts_set() helper. Said helper would initialize options_len properly before copying data into options, however in the GRE ERSPAN code a partial update is done, preventing the use of the helper function. Before this change the handling of ERSPAN traffic in GRE tunnels would cause a kernel panic when the kernel is compiled with GCC 15+ and having FORTIFY_SOURCE configured: memcpy: detected buffer overflow: 4 byte write of buffer size 0 Call Trace: __fortify_panic+0xd/0xf erspan_rcv.cold+0x68/0x83 ? ip_route_input_slow+0x816/0x9d0 gre_rcv+0x1b2/0x1c0 gre_rcv+0x8e/0x100 ? raw_v4_input+0x2a0/0x2b0 ip_protocol_deliver_rcu+0x1ea/0x210 ip_local_deliver_finish+0x86/0x110 ip_local_deliver+0x65/0x110 ? ip_rcv_finish_core+0xd6/0x360 ip_rcv+0x186/0x1a0 Cc: stable@vger.kernel.org Link: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-counted_005fby-variable-attribute Reported-at: https://launchpad.net/bugs/2129580 Fixes: bb5e62f2d547 ("net: Add options as a flexible array to struct ip_tunnel_info") Signed-off-by: Frode Nordahl Reviewed-by: Simon Horman Signed-off-by: NipaLocal --- net/ipv4/ip_gre.c | 6 ++++-- net/ipv6/ip6_gre.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 761a53c6a89a6..8178c44a3cdd4 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -330,6 +330,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, if (!tun_dst) return PACKET_REJECT; + /* MUST set options_len before referencing options */ + info = &tun_dst->u.tun_info; + info->options_len = sizeof(*md); + /* skb can be uncloned in __iptunnel_pull_header, so * old pkt_md is no longer valid and we need to reset * it @@ -344,10 +348,8 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE); - info = &tun_dst->u.tun_info; __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, info->key.tun_flags); - info->options_len = sizeof(*md); } skb_reset_mac_header(skb); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 8bc3f05f594ed..d19d86ed43766 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -535,6 +535,10 @@ static int ip6erspan_rcv(struct sk_buff *skb, if (!tun_dst) return PACKET_REJECT; + /* MUST set options_len before referencing options */ + info = &tun_dst->u.tun_info; + info->options_len = sizeof(*md); + /* skb can be uncloned in __iptunnel_pull_header, so * old pkt_md is no longer valid and we need to reset * it @@ -543,7 +547,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, skb_network_header_len(skb); pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len + sizeof(*ershdr)); - info = &tun_dst->u.tun_info; md = ip_tunnel_info_opts(info); md->version = ver; md2 = &md->u.md2; @@ -551,7 +554,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, ERSPAN_V2_MDSIZE); __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, info->key.tun_flags); - info->options_len = sizeof(*md); ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); From 2a9cf46c499533b1b1536a34a95aefbdb5bd2730 Mon Sep 17 00:00:00 2001 From: Kathara Sasikumar Date: Sun, 14 Dec 2025 00:13:39 +0000 Subject: [PATCH 114/214] mac802154: fix uninitialized security header fields KMSAN reported an uninitialized-value access in ieee802154_hdr_push_sechdr(). This happened because mac802154_set_header_security() allowed frames with cb->secen=1 but LLSEC disabled when secen_override=0, leaving parts of the security header uninitialized. Fix the validation so security-enabled frames are rejected whenever LLSEC is disabled, regardless of secen_override. Also clear the full header struct in the header creation functions to avoid partial initialization. Reported-by: syzbot+60a66d44892b66b56545@syzkaller.appspotmail.com Tested-by: syzbot+60a66d44892b66b56545@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=60a66d44892b66b56545 Signed-off-by: Kathara Sasikumar Signed-off-by: NipaLocal --- net/mac802154/iface.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 9e4631fade90c..a1222c1b62b3c 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -328,8 +328,14 @@ static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata, mac802154_llsec_get_params(&sdata->sec, ¶ms); - if (!params.enabled && cb->secen_override && cb->secen) - return -EINVAL; + if (!cb->secen_override) { + if (!params.enabled) + return 0; + } else { + if (cb->secen && !params.enabled) + return -EINVAL; + } + if (!params.enabled || (cb->secen_override && !cb->secen) || !params.out_level) @@ -366,7 +372,7 @@ static int ieee802154_header_create(struct sk_buff *skb, if (!daddr) return -EINVAL; - memset(&hdr.fc, 0, sizeof(hdr.fc)); + memset(&hdr, 0, sizeof(hdr)); hdr.fc.type = cb->type; hdr.fc.security_enabled = cb->secen; hdr.fc.ack_request = cb->ackreq; @@ -432,7 +438,7 @@ static int mac802154_header_create(struct sk_buff *skb, if (!daddr) return -EINVAL; - memset(&hdr.fc, 0, sizeof(hdr.fc)); + memset(&hdr, 0, sizeof(hdr)); hdr.fc.type = IEEE802154_FC_TYPE_DATA; hdr.fc.ack_request = wpan_dev->ackreq; hdr.seq = atomic_inc_return(&dev->ieee802154_ptr->dsn) & 0xFF; From 6e686ddb1611783d890faca214453d4e98a3cd41 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 14 Dec 2025 10:30:07 +0100 Subject: [PATCH 115/214] net: airoha: Move net_devs registration in a dedicated routine Since airoha_probe() is not executed under rtnl lock, there is small race where a given device is configured by user-space while the remaining ones are not completely loaded from the dts yet. This condition will allow a hw device misconfiguration since there are some conditions (e.g. GDM2 check in airoha_dev_init()) that require all device are properly loaded from the device tree. Fix the issue moving net_devices registration at the end of the airoha_probe routine. Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") Signed-off-by: Lorenzo Bianconi Reviewed-by: Simon Horman Signed-off-by: NipaLocal --- drivers/net/ethernet/airoha/airoha_eth.c | 39 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 75893c90a0a17..315d97036ac1d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2924,19 +2924,26 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, port->id = id; eth->ports[p] = port; - err = airoha_metadata_dst_alloc(port); - if (err) - return err; + return airoha_metadata_dst_alloc(port); +} - err = register_netdev(dev); - if (err) - goto free_metadata_dst; +static int airoha_register_gdm_devices(struct airoha_eth *eth) +{ + int i; - return 0; + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int err; -free_metadata_dst: - airoha_metadata_dst_free(port); - return err; + if (!port) + continue; + + err = register_netdev(port->dev); + if (err) + return err; + } + + return 0; } static int airoha_probe(struct platform_device *pdev) @@ -3027,6 +3034,10 @@ static int airoha_probe(struct platform_device *pdev) } } + err = airoha_register_gdm_devices(eth); + if (err) + goto error_napi_stop; + return 0; error_napi_stop: @@ -3040,10 +3051,12 @@ static int airoha_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - if (port && port->dev->reg_state == NETREG_REGISTERED) { + if (!port) + continue; + + if (port->dev->reg_state == NETREG_REGISTERED) unregister_netdev(port->dev); - airoha_metadata_dst_free(port); - } + airoha_metadata_dst_free(port); } free_netdev(eth->napi_dev); platform_set_drvdata(pdev, NULL); From f58ca9378c744d82a53653e5fa5435ec69c8a46f Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Sun, 14 Dec 2025 21:17:26 +0800 Subject: [PATCH 116/214] NFC: Fix error handling in nfc_genl_dump_targets nfc_genl_dump_targets() increments the device reference count via nfc_get_device() but fails to decrement it properly. nfc_get_device() calls class_find_device() which internally calls get_device() to increment the reference count. No corresponding put_device() is made to decrement the reference count. Add proper reference count decrementing using nfc_put_device() when the dump operation completes or encounters an error, ensuring balanced reference counting. Found by code review. Cc: stable@vger.kernel.org Fixes: 4d12b8b129f1 ("NFC: add nfc generic netlink interface") Signed-off-by: Ma Ke Signed-off-by: NipaLocal --- net/nfc/netlink.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index a18e2c503da61..9ae138ee91dde 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -159,6 +159,11 @@ static int nfc_genl_dump_targets(struct sk_buff *skb, cb->args[0] = i; + if (rc < 0 || i >= dev->n_targets) { + nfc_put_device(dev); + cb->args[1] = 0; + } + return skb->len; } From 776c987b1b8d5bd732a0e8f7df08972e82fb37cc Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 15 Dec 2025 17:02:35 +0200 Subject: [PATCH 117/214] net: dsa: properly keep track of conduit reference Problem description ------------------- DSA has a mumbo-jumbo of reference handling of the conduit net device and its kobject which, sadly, is just wrong and doesn't make sense. There are two distinct problems. 1. The OF path, which uses of_find_net_device_by_node(), never releases the elevated refcount on the conduit's kobject. Nominally, the OF and non-OF paths should result in objects having identical reference counts taken, and it is already suspicious that dsa_dev_to_net_device() has a put_device() call which is missing in dsa_port_parse_of(), but we can actually even verify that an issue exists. With CONFIG_DEBUG_KOBJECT_RELEASE=y, if we run this command "before" and "after" applying this patch: (unbind the conduit driver for net device eno2) echo 0000:00:00.2 > /sys/bus/pci/drivers/fsl_enetc/unbind we see these lines in the output diff which appear only with the patch applied: kobject: 'eno2' (ffff002009a3a6b8): kobject_release, parent 0000000000000000 (delayed 1000) kobject: '109' (ffff0020099d59a0): kobject_release, parent 0000000000000000 (delayed 1000) 2. After we find the conduit interface one way (OF) or another (non-OF), it can get unregistered at any time, and DSA remains with a long-lived, but in this case stale, cpu_dp->conduit pointer. Holding the net device's underlying kobject isn't actually of much help, it just prevents it from being freed (but we never need that kobject directly). What helps us to prevent the net device from being unregistered is the parallel netdev reference mechanism (dev_hold() and dev_put()). Actually we actually use that netdev tracker mechanism implicitly on user ports since commit 2f1e8ea726e9 ("net: dsa: link interfaces with the DSA master to get rid of lockdep warnings"), via netdev_upper_dev_link(). But time still passes at DSA switch probe time between the initial of_find_net_device_by_node() code and the user port creation time, time during which the conduit could unregister itself and DSA wouldn't know about it. So we have to run of_find_net_device_by_node() under rtnl_lock() to prevent that from happening, and release the lock only with the netdev tracker having acquired the reference. Do we need to keep the reference until dsa_unregister_switch() / dsa_switch_shutdown()? 1: Maybe yes. A switch device will still be registered even if all user ports failed to probe, see commit 86f8b1c01a0a ("net: dsa: Do not make user port errors fatal"), and the cpu_dp->conduit pointers remain valid. I haven't audited all call paths to see whether they will actually use the conduit in lack of any user port, but if they do, it seems safer to not rely on user ports for that reference. 2. Definitely yes. We support changing the conduit which a user port is associated to, and we can get into a situation where we've moved all user ports away from a conduit, thus no longer hold any reference to it via the net device tracker. But we shouldn't let it go nonetheless - see the next change in relation to dsa_tree_find_first_conduit() and LAG conduits which disappear. We have to be prepared to return to the physical conduit, so the CPU port must explicitly keep another reference to it. This is also to say: the user ports and their CPU ports may not always keep a reference to the same conduit net device, and both are needed. As for the conduit's kobject for the /sys/class/net/ entry, we don't care about it, we can release it as soon as we hold the net device object itself. History and blame attribution ----------------------------- The code has been refactored so many times, it is very difficult to follow and properly attribute a blame, but I'll try to make a short history which I hope to be correct. We have two distinct probing paths: - one for OF, introduced in 2016 in commit 83c0afaec7b7 ("net: dsa: Add new binding implementation") - one for non-OF, introduced in 2017 in commit 71e0bbde0d88 ("net: dsa: Add support for platform data") These are both complete rewrites of the original probing paths (which used struct dsa_switch_driver and other weird stuff, instead of regular devices on their respective buses for register access, like MDIO, SPI, I2C etc): - one for OF, introduced in 2013 in commit 5e95329b701c ("dsa: add device tree bindings to register DSA switches") - one for non-OF, introduced in 2008 in commit 91da11f870f0 ("net: Distributed Switch Architecture protocol support") except for tiny bits and pieces like dsa_dev_to_net_device() which were seemingly carried over since the original commit, and used to this day. The point is that the original probing paths received a fix in 2015 in the form of commit 679fb46c5785 ("net: dsa: Add missing master netdev dev_put() calls"), but the fix never made it into the "new" (dsa2) probing paths that can still be traced to today, and the fixed probing path was later deleted in 2019 in commit 93e86b3bc842 ("net: dsa: Remove legacy probing support"). That is to say, the new probing paths were never quite correct in this area. The existence of the legacy probing support which was deleted in 2019 explains why dsa_dev_to_net_device() returns a conduit with elevated refcount (because it was supposed to be released during dsa_remove_dst()). After the removal of the legacy code, the only user of dsa_dev_to_net_device() calls dev_put(conduit) immediately after this function returns. This pattern makes no sense today, and can only be interpreted historically to understand why dev_hold() was there in the first place. Change details -------------- Today we have a better netdev tracking infrastructure which we should use. Logically netdev_hold() belongs in common code (dsa_port_parse_cpu(), where dp->conduit is assigned), but there is a tradeoff to be made with the rtnl_lock() section which would become a bit too long if we did that - dsa_port_parse_cpu() also calls request_module(). So we duplicate a bit of logic in order for the callers of dsa_port_parse_cpu() to be the ones responsible of holding the conduit reference and releasing it on error. This shortens the rtnl_lock() section significantly. In the dsa_switch_probe() error path, dsa_switch_release_ports() will be called in a number of situations, one being where dsa_port_parse_cpu() maybe didn't get the chance to run at all (a different port failed earlier, etc). So we have to test for the conduit being NULL prior to calling netdev_put(). There have still been so many transformations to the code since the blamed commits (rename master -> conduit, commit 0650bf52b31f ("net: dsa: be compatible with masters which unregister on shutdown")), that it only makes sense to fix the code using the best methods available today and see how it can be backported to stable later. I suspect the fix cannot even be backported to kernels which lack dsa_switch_shutdown(), and I suspect this is also maybe why the long-lived conduit reference didn't make it into the new DSA probing paths at the time (problems during shutdown). Because dsa_dev_to_net_device() has a single call site and has to be changed anyway, the logic was just absorbed into the non-OF dsa_port_parse(). Tested on the ocelot/felix switch and on dsa_loop, both on the NXP LS1028A with CONFIG_DEBUG_KOBJECT_RELEASE=y. Reported-by: Ma Ke Closes: https://lore.kernel.org/netdev/20251214131204.4684-1-make24@iscas.ac.cn/ Fixes: 83c0afaec7b7 ("net: dsa: Add new binding implementation") Fixes: 71e0bbde0d88 ("net: dsa: Add support for platform data") Reviewed-by: Jonas Gorski Signed-off-by: Vladimir Oltean Signed-off-by: NipaLocal --- include/net/dsa.h | 1 + net/dsa/dsa.c | 59 +++++++++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index cced1a8667578..6b2b5ed64ea4c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -302,6 +302,7 @@ struct dsa_port { struct devlink_port devlink_port; struct phylink *pl; struct phylink_config pl_config; + netdevice_tracker conduit_tracker; struct dsa_lag *lag; struct net_device *hsr_dev; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index a20efabe778fc..50b3fceb5c04d 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1253,14 +1253,25 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) if (ethernet) { struct net_device *conduit; const char *user_protocol; + int err; + rtnl_lock(); conduit = of_find_net_device_by_node(ethernet); of_node_put(ethernet); - if (!conduit) + if (!conduit) { + rtnl_unlock(); return -EPROBE_DEFER; + } + + netdev_hold(conduit, &dp->conduit_tracker, GFP_KERNEL); + put_device(&conduit->dev); + rtnl_unlock(); user_protocol = of_get_property(dn, "dsa-tag-protocol", NULL); - return dsa_port_parse_cpu(dp, conduit, user_protocol); + err = dsa_port_parse_cpu(dp, conduit, user_protocol); + if (err) + netdev_put(conduit, &dp->conduit_tracker); + return err; } if (link) @@ -1393,37 +1404,30 @@ static struct device *dev_find_class(struct device *parent, char *class) return device_find_child(parent, class, dev_is_class); } -static struct net_device *dsa_dev_to_net_device(struct device *dev) -{ - struct device *d; - - d = dev_find_class(dev, "net"); - if (d != NULL) { - struct net_device *nd; - - nd = to_net_dev(d); - dev_hold(nd); - put_device(d); - - return nd; - } - - return NULL; -} - static int dsa_port_parse(struct dsa_port *dp, const char *name, struct device *dev) { if (!strcmp(name, "cpu")) { struct net_device *conduit; + struct device *d; + int err; - conduit = dsa_dev_to_net_device(dev); - if (!conduit) + rtnl_lock(); + d = dev_find_class(dev, "net"); + if (!d) { + rtnl_unlock(); return -EPROBE_DEFER; + } - dev_put(conduit); + conduit = to_net_dev(d); + netdev_hold(conduit, &dp->conduit_tracker, GFP_KERNEL); + put_device(d); + rtnl_unlock(); - return dsa_port_parse_cpu(dp, conduit, NULL); + err = dsa_port_parse_cpu(dp, conduit, NULL); + if (err) + netdev_put(conduit, &dp->conduit_tracker); + return err; } if (!strcmp(name, "dsa")) @@ -1491,6 +1495,9 @@ static void dsa_switch_release_ports(struct dsa_switch *ds) struct dsa_vlan *v, *n; dsa_switch_for_each_port_safe(dp, next, ds) { + if (dsa_port_is_cpu(dp) && dp->conduit) + netdev_put(dp->conduit, &dp->conduit_tracker); + /* These are either entries that upper layers lost track of * (probably due to bugs), or installed through interfaces * where one does not necessarily have to remove them, like @@ -1635,8 +1642,10 @@ void dsa_switch_shutdown(struct dsa_switch *ds) /* Disconnect from further netdevice notifiers on the conduit, * since netdev_uses_dsa() will now return false. */ - dsa_switch_for_each_cpu_port(dp, ds) + dsa_switch_for_each_cpu_port(dp, ds) { dp->conduit->dsa_ptr = NULL; + netdev_put(dp->conduit, &dp->conduit_tracker); + } rtnl_unlock(); out: From 8a3d32dd879c17465970bbf98c855ca3889df21c Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 15 Dec 2025 17:02:36 +0200 Subject: [PATCH 118/214] net: dsa: fix missing put_device() in dsa_tree_find_first_conduit() of_find_net_device_by_node() searches net devices by their /sys/class/net/, entry. It is documented in its kernel-doc that: * If successful, returns a pointer to the net_device with the embedded * struct device refcount incremented by one, or NULL on failure. The * refcount must be dropped when done with the net_device. We are missing a put_device(&conduit->dev) which we could place at the end of dsa_tree_find_first_conduit(). But to explain why calling put_device() right away is safe is the same as to explain why the chosen solution is different. The code is very poorly split: dsa_tree_find_first_conduit() was first introduced in commit 95f510d0b792 ("net: dsa: allow the DSA master to be seen and changed through rtnetlink") but was first used several commits later, in commit acc43b7bf52a ("net: dsa: allow masters to join a LAG"). Assume there is a switch with 2 CPU ports and 2 conduits, eno2 and eno3. When we create a LAG (bonding or team device) and place eno2 and eno3 beneath it, we create a 3rd conduit (the LAG device itself), but this is slightly different than the first two. Namely, the cpu_dp->conduit pointer of the CPU ports does not change, and remains pointing towards the physical Ethernet controllers which are now LAG ports. Only 2 things change: - the LAG device has a dev->dsa_ptr which marks it as a DSA conduit - dsa_port_to_conduit(user port) finds the LAG and not the physical conduit, because of the dp->cpu_port_in_lag bit being set. When the LAG device is destroyed, dsa_tree_migrate_ports_from_lag_conduit() is called and this is where dsa_tree_find_first_conduit() kicks in. This is the logical mistake and the reason why introducing code in one patch and using it from another is bad practice. I didn't realize that I don't have to call of_find_net_device_by_node() again; the cpu_dp->conduit association was never undone, and is still available for direct (re)use. There's only one concern - maybe the conduit disappeared in the meantime, but the netdev_hold() call we made during dsa_port_parse_cpu() (see previous change) ensures that this was not the case. Therefore, fixing the code means reimplementing it in the simplest way. I am blaming the time of use, since this is what "git blame" would show if we were to monitor for the conduit's kobject's refcount remaining elevated instead of being freed. Tested on the NXP LS1028A, using the steps from Documentation/networking/dsa/configuration.rst section "Affinity of user ports to CPU ports", followed by (extra prints added by me): $ ip link del bond0 mscc_felix 0000:00:00.5 swp3: Link is Down bond0 (unregistering): (slave eno2): Releasing backup interface fsl_enetc 0000:00:00.2 eno2: Link is Down mscc_felix 0000:00:00.5 swp0: bond0 disappeared, migrating to eno2 mscc_felix 0000:00:00.5 swp1: bond0 disappeared, migrating to eno2 mscc_felix 0000:00:00.5 swp2: bond0 disappeared, migrating to eno2 mscc_felix 0000:00:00.5 swp3: bond0 disappeared, migrating to eno2 Fixes: acc43b7bf52a ("net: dsa: allow masters to join a LAG") Signed-off-by: Vladimir Oltean Signed-off-by: NipaLocal --- net/dsa/dsa.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 50b3fceb5c04d..99ede37698ac5 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -367,16 +367,10 @@ static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) struct net_device *dsa_tree_find_first_conduit(struct dsa_switch_tree *dst) { - struct device_node *ethernet; - struct net_device *conduit; struct dsa_port *cpu_dp; cpu_dp = dsa_tree_find_first_cpu(dst); - ethernet = of_parse_phandle(cpu_dp->dn, "ethernet", 0); - conduit = of_find_net_device_by_node(ethernet); - of_node_put(ethernet); - - return conduit; + return cpu_dp->conduit; } /* Assign the default CPU port (the first one in the tree) to all ports of the From 57fd36b7873676e57c47f22cef9abaa596bcccb0 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Mon, 15 Dec 2025 20:47:28 +0530 Subject: [PATCH 119/214] amd-xgbe: reset retries and mode on RX adapt failures During the stress tests, early RX adaptation handshakes can fail, such as missing the RX_ADAPT ACK or not receiving a coefficient update before block lock is established. Continuing to retry RX adaptation in this state is often ineffective if the current mode selection is not viable. Resetting the RX adaptation retry counter when an RX_ADAPT request fails to receive ACK or a coefficient update prior to block lock, and clearing mode_set so the next bring-up performs a fresh mode selection rather than looping on a likely invalid configuration. Fixes: 4f3b20bfbb75 ("amd-xgbe: add support for rx-adaptation") Signed-off-by: Raju Rangoju Reviewed-by: Simon Horman Reviewed-by: Shyam Sundar S K Signed-off-by: NipaLocal --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index a68757e8fd22c..c63ddb12237ea 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -1928,6 +1928,7 @@ static void xgbe_set_rx_adap_mode(struct xgbe_prv_data *pdata, { if (pdata->rx_adapt_retries++ >= MAX_RX_ADAPT_RETRIES) { pdata->rx_adapt_retries = 0; + pdata->mode_set = false; return; } @@ -1974,6 +1975,7 @@ static void xgbe_rx_adaptation(struct xgbe_prv_data *pdata) */ netif_dbg(pdata, link, pdata->netdev, "Block_lock done"); pdata->rx_adapt_done = true; + pdata->rx_adapt_retries = 0; pdata->mode_set = false; return; } From 51b67a2f3e4f1204416fdaab768288e613af5235 Mon Sep 17 00:00:00 2001 From: "weiming shi (Swing)" Date: Tue, 16 Dec 2025 00:12:31 +0800 Subject: [PATCH 120/214] net: skbuff: fix usercopy violation in skbuff_fclone_cache net: skbuff: add usercopy region to skbuff_fclone_cache skbuff_fclone_cache was created without defining a usercopy region, unlike skbuff_head_cache which properly whitelists the cb[] field. This causes a usercopy BUG() when CONFIG_HARDENED_USERCOPY is enabled and the kernel attempts to copy sk_buff.cb data to userspace via sock_recv_errqueue() -> put_cmsg(). The crash occurs when: 1. TCP allocates an skb using alloc_skb_fclone() (from skbuff_fclone_cache) 2. The skb is cloned via skb_clone() using the pre-allocated fclone 3. The cloned skb is queued to sk_error_queue for timestamp reporting 4. Userspace reads the error queue via recvmsg(MSG_ERRQUEUE) 5. sock_recv_errqueue() calls put_cmsg() to copy serr->ee from skb->cb 6. __check_heap_object() fails because skbuff_fclone_cache has no usercopy whitelist When cloned skbs allocated from skbuff_fclone_cache are used in the socket error queue, accessing the sock_exterr_skb structure in skb->cb via put_cmsg() triggers a usercopy hardening violation: [ 5.379589] usercopy: Kernel memory exposure attempt detected from SLUB object 'skbuff_fclone_cache' (offset 296, size 16)! [ 5.382796] kernel BUG at mm/usercopy.c:102! [ 5.383923] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI [ 5.384903] CPU: 1 UID: 0 PID: 138 Comm: poc_put_cmsg Not tainted 6.12.57 #7 [ 5.384903] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 [ 5.384903] RIP: 0010:usercopy_abort+0x6c/0x80 [ 5.384903] Code: 1a 86 51 48 c7 c2 40 15 1a 86 41 52 48 c7 c7 c0 15 1a 86 48 0f 45 d6 48 c7 c6 80 15 1a 86 48 89 c1 49 0f 45 f3 e8 84 27 88 ff <0f> 0b 490 [ 5.384903] RSP: 0018:ffffc900006f77a8 EFLAGS: 00010246 [ 5.384903] RAX: 000000000000006f RBX: ffff88800f0ad2a8 RCX: 1ffffffff0f72e74 [ 5.384903] RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffff87b973a0 [ 5.384903] RBP: 0000000000000010 R08: 0000000000000000 R09: fffffbfff0f72e74 [ 5.384903] R10: 0000000000000003 R11: 79706f6372657375 R12: 0000000000000001 [ 5.384903] R13: ffff88800f0ad2b8 R14: ffffea00003c2b40 R15: ffffea00003c2b00 [ 5.384903] FS: 0000000011bc4380(0000) GS:ffff8880bf100000(0000) knlGS:0000000000000000 [ 5.384903] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 5.384903] CR2: 000056aa3b8e5fe4 CR3: 000000000ea26004 CR4: 0000000000770ef0 [ 5.384903] PKRU: 55555554 [ 5.384903] Call Trace: [ 5.384903] [ 5.384903] __check_heap_object+0x9a/0xd0 [ 5.384903] __check_object_size+0x46c/0x690 [ 5.384903] put_cmsg+0x129/0x5e0 [ 5.384903] sock_recv_errqueue+0x22f/0x380 [ 5.384903] tls_sw_recvmsg+0x7ed/0x1960 [ 5.384903] ? srso_alias_return_thunk+0x5/0xfbef5 [ 5.384903] ? schedule+0x6d/0x270 [ 5.384903] ? srso_alias_return_thunk+0x5/0xfbef5 [ 5.384903] ? mutex_unlock+0x81/0xd0 [ 5.384903] ? __pfx_mutex_unlock+0x10/0x10 [ 5.384903] ? __pfx_tls_sw_recvmsg+0x10/0x10 [ 5.384903] ? _raw_spin_lock_irqsave+0x8f/0xf0 [ 5.384903] ? _raw_read_unlock_irqrestore+0x20/0x40 [ 5.384903] ? srso_alias_return_thunk+0x5/0xfbef5 Fix by using kmem_cache_create_usercopy() with the same cb[] region whitelist as skbuff_head_cache. Signed-off-by: weiming shi (Swing) Reported-by: Xiang Mei xmei5@asu.edu Reported-by: weiming shi (Swing) bestswngs@gmail.com Signed-off-by: NipaLocal --- net/core/skbuff.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a00808f7be6a1..419bda42560ad 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -622,7 +622,7 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, return obj; } -/* Allocate a new skbuff. We do this ourselves so we can fill in a few +/* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the * [BEEP] leaks. * @@ -5157,11 +5157,14 @@ void __init skb_init(void) NULL); skbuff_cache_size = kmem_cache_size(net_hotdata.skbuff_cache); - net_hotdata.skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", - sizeof(struct sk_buff_fclones), - 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, - NULL); + net_hotdata.skbuff_fclone_cache = kmem_cache_create_usercopy("skbuff_fclone_cache", + sizeof(struct sk_buff_fclones), + 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, + offsetof(struct sk_buff, cb), + sizeof(struct sk_buff) + sizeof_field(struct sk_buff, cb), + NULL); + /* usercopy should only access first SKB_SMALL_HEAD_HEADROOM bytes. * struct skb_shared_info is located at the end of skb->head, * and should not be copied to/from user. From f7055c130d4cc09856ef607b5fd6e9706e3123f6 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 15 Dec 2025 19:54:46 +0300 Subject: [PATCH 121/214] ipvlan: Make the addrs_lock be per port Make the addrs_lock be per port, not per ipvlan dev. Initial code seems to be written in the assumption, that any address change must occur under RTNL. But it is not so for the case of IPv6. So 1) Introduce per-port addrs_lock. 2) It was needed to fix places where it was forgotten to take lock (ipvlan_open/ipvlan_close) 3) Fix places, where list_for_each_entry_rcu() was used to iterate the list while holding a lock This appears to be a very minor problem though. Since it's highly unlikely that ipvlan_add_addr() will be called on 2 CPU simultaneously. But nevertheless, this could cause: 1) False-negative of ipvlan_addr_busy(): one interface iterated through all port->ipvlans + ipvlan->addrs under some ipvlan spinlock, and another added IP under its own lock. Though this is only possible for IPv6, since looks like only ipvlan_addr6_event() can be called without rtnl_lock. 2) Race since ipvlan_ht_addr_add(port) is called under different ipvlan->addrs_lock locks This should not affect performance, since add/remove IP is a rare situation and spinlock is not taken on fast paths. Fixes: 8230819494b3 ("ipvlan: use per device spinlock to protect addrs list updates") Signed-off-by: Dmitry Skorodumov CC: Paolo Abeni Signed-off-by: Dmitry Skorodumov Signed-off-by: NipaLocal --- drivers/net/ipvlan/ipvlan.h | 2 +- drivers/net/ipvlan/ipvlan_core.c | 12 ++++---- drivers/net/ipvlan/ipvlan_main.c | 52 ++++++++++++++++++-------------- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 50de3ee204dbc..80f84fc87008b 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -69,7 +69,6 @@ struct ipvl_dev { DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE); netdev_features_t sfeatures; u32 msg_enable; - spinlock_t addrs_lock; }; struct ipvl_addr { @@ -90,6 +89,7 @@ struct ipvl_port { struct net_device *dev; possible_net_t pnet; struct hlist_head hlhead[IPVLAN_HASH_SIZE]; + spinlock_t addrs_lock; /* guards hash-table and addrs */ struct list_head ipvlans; u16 mode; u16 flags; diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 2efa3ba148aa7..22cb5ee7a2310 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -109,14 +109,14 @@ struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, { struct ipvl_addr *addr, *ret = NULL; - rcu_read_lock(); - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) { + assert_spin_locked(&ipvlan->port->addrs_lock); + + list_for_each_entry(addr, &ipvlan->addrs, anode) { if (addr_equal(is_v6, addr, iaddr)) { ret = addr; break; } } - rcu_read_unlock(); return ret; } @@ -125,14 +125,14 @@ bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6) struct ipvl_dev *ipvlan; bool ret = false; - rcu_read_lock(); - list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) { + assert_spin_locked(&port->addrs_lock); + + list_for_each_entry(ipvlan, &port->ipvlans, pnode) { if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) { ret = true; break; } } - rcu_read_unlock(); return ret; } diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 660f3db117664..b0b4f747f162e 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -75,6 +75,7 @@ static int ipvlan_port_create(struct net_device *dev) for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++) INIT_HLIST_HEAD(&port->hlhead[idx]); + spin_lock_init(&port->addrs_lock); skb_queue_head_init(&port->backlog); INIT_WORK(&port->wq, ipvlan_process_multicast); ida_init(&port->ida); @@ -181,18 +182,18 @@ static void ipvlan_uninit(struct net_device *dev) static int ipvlan_open(struct net_device *dev) { struct ipvl_dev *ipvlan = netdev_priv(dev); + struct ipvl_port *port = ipvlan->port; struct ipvl_addr *addr; - if (ipvlan->port->mode == IPVLAN_MODE_L3 || - ipvlan->port->mode == IPVLAN_MODE_L3S) + if (port->mode == IPVLAN_MODE_L3 || port->mode == IPVLAN_MODE_L3S) dev->flags |= IFF_NOARP; else dev->flags &= ~IFF_NOARP; - rcu_read_lock(); - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) + spin_lock_bh(&port->addrs_lock); + list_for_each_entry(addr, &ipvlan->addrs, anode) ipvlan_ht_addr_add(ipvlan, addr); - rcu_read_unlock(); + spin_unlock_bh(&port->addrs_lock); return 0; } @@ -206,10 +207,10 @@ static int ipvlan_stop(struct net_device *dev) dev_uc_unsync(phy_dev, dev); dev_mc_unsync(phy_dev, dev); - rcu_read_lock(); - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) + spin_lock_bh(&ipvlan->port->addrs_lock); + list_for_each_entry(addr, &ipvlan->addrs, anode) ipvlan_ht_addr_del(addr); - rcu_read_unlock(); + spin_unlock_bh(&ipvlan->port->addrs_lock); return 0; } @@ -579,7 +580,6 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params, if (!tb[IFLA_MTU]) ipvlan_adjust_mtu(ipvlan, phy_dev); INIT_LIST_HEAD(&ipvlan->addrs); - spin_lock_init(&ipvlan->addrs_lock); /* TODO Probably put random address here to be presented to the * world but keep using the physical-dev address for the outgoing @@ -657,13 +657,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head) struct ipvl_dev *ipvlan = netdev_priv(dev); struct ipvl_addr *addr, *next; - spin_lock_bh(&ipvlan->addrs_lock); + spin_lock_bh(&ipvlan->port->addrs_lock); list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { ipvlan_ht_addr_del(addr); list_del_rcu(&addr->anode); kfree_rcu(addr, rcu); } - spin_unlock_bh(&ipvlan->addrs_lock); + spin_unlock_bh(&ipvlan->port->addrs_lock); ida_free(&ipvlan->port->ida, dev->dev_id); list_del_rcu(&ipvlan->pnode); @@ -817,6 +817,8 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) { struct ipvl_addr *addr; + assert_spin_locked(&ipvlan->port->addrs_lock); + addr = kzalloc(sizeof(struct ipvl_addr), GFP_ATOMIC); if (!addr) return -ENOMEM; @@ -847,16 +849,16 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) { struct ipvl_addr *addr; - spin_lock_bh(&ipvlan->addrs_lock); + spin_lock_bh(&ipvlan->port->addrs_lock); addr = ipvlan_find_addr(ipvlan, iaddr, is_v6); if (!addr) { - spin_unlock_bh(&ipvlan->addrs_lock); + spin_unlock_bh(&ipvlan->port->addrs_lock); return; } ipvlan_ht_addr_del(addr); list_del_rcu(&addr->anode); - spin_unlock_bh(&ipvlan->addrs_lock); + spin_unlock_bh(&ipvlan->port->addrs_lock); kfree_rcu(addr, rcu); } @@ -878,14 +880,14 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { int ret = -EINVAL; - spin_lock_bh(&ipvlan->addrs_lock); + spin_lock_bh(&ipvlan->port->addrs_lock); if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv6=%pI6c addr for %s intf\n", ip6_addr, ipvlan->dev->name); else ret = ipvlan_add_addr(ipvlan, ip6_addr, true); - spin_unlock_bh(&ipvlan->addrs_lock); + spin_unlock_bh(&ipvlan->port->addrs_lock); return ret; } @@ -924,21 +926,24 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused, struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr; struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev; struct ipvl_dev *ipvlan = netdev_priv(dev); + int ret = NOTIFY_OK; if (!ipvlan_is_valid_dev(dev)) return NOTIFY_DONE; switch (event) { case NETDEV_UP: + spin_lock_bh(&ipvlan->port->addrs_lock); if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) { NL_SET_ERR_MSG(i6vi->extack, "Address already assigned to an ipvlan device"); - return notifier_from_errno(-EADDRINUSE); + ret = notifier_from_errno(-EADDRINUSE); } + spin_unlock_bh(&ipvlan->port->addrs_lock); break; } - return NOTIFY_OK; + return ret; } #endif @@ -946,14 +951,14 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) { int ret = -EINVAL; - spin_lock_bh(&ipvlan->addrs_lock); + spin_lock_bh(&ipvlan->port->addrs_lock); if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv4=%pI4 on %s intf.\n", ip4_addr, ipvlan->dev->name); else ret = ipvlan_add_addr(ipvlan, ip4_addr, false); - spin_unlock_bh(&ipvlan->addrs_lock); + spin_unlock_bh(&ipvlan->port->addrs_lock); return ret; } @@ -995,21 +1000,24 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused, struct in_validator_info *ivi = (struct in_validator_info *)ptr; struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev; struct ipvl_dev *ipvlan = netdev_priv(dev); + int ret = NOTIFY_OK; if (!ipvlan_is_valid_dev(dev)) return NOTIFY_DONE; switch (event) { case NETDEV_UP: + spin_lock_bh(&ipvlan->port->addrs_lock); if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) { NL_SET_ERR_MSG(ivi->extack, "Address already assigned to an ipvlan device"); - return notifier_from_errno(-EADDRINUSE); + ret = notifier_from_errno(-EADDRINUSE); } + spin_unlock_bh(&ipvlan->port->addrs_lock); break; } - return NOTIFY_OK; + return ret; } static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = { From 0b194765a0934410df1a18c68f906573b291b578 Mon Sep 17 00:00:00 2001 From: Di Zhu Date: Tue, 16 Dec 2025 16:52:10 +0800 Subject: [PATCH 122/214] netdev: increment TSO only if TSO is not enabled on any slave device Unconditionally increment the TSO flag has a side effect: it will also directly clear the flags in NETIF_F_ALL_FOR_ALL on the master device, which can cause issues such as the inability to enable the nocache copy feature on the bonding network card. So, when at least one slave device's TSO is enabled, there is no need to explicitly increment the TSO flag to the master device. Fixes: b0ce3508b25e ("bonding: allow TSO being set on bonding master") Signed-off-by: Di Zhu Signed-off-by: NipaLocal --- include/linux/netdevice.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5870a9e514a51..660f05868654a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -5323,7 +5323,8 @@ netdev_features_t netdev_increment_features(netdev_features_t all, static inline netdev_features_t netdev_add_tso_features(netdev_features_t features, netdev_features_t mask) { - return netdev_increment_features(features, NETIF_F_ALL_TSO, mask); + return (features & NETIF_F_ALL_TSO) ? features : + netdev_increment_features(features, NETIF_F_ALL_TSO, mask); } int __netdev_update_features(struct net_device *dev); From 35f4fd41c2b2f0978c20cd41acf24c9d2dcb13e9 Mon Sep 17 00:00:00 2001 From: Abdullah Alomani Date: Tue, 16 Dec 2025 12:20:36 +0300 Subject: [PATCH 123/214] net: docs: fix grammar in CAIF stack description Corrected "handled as by the rest of the layers" to "handled like the rest of the layers" to clearly indicate that the transmit and receive behavior of this layer follows the same pattern as other layers in the CAIF stack. This makes it explicit that no additional layer-specific handling occurs, improving clarity for readers and developers implementing or maintaining CAIF layers. Signed-off-by: Abdullah Alomani Signed-off-by: NipaLocal --- Documentation/networking/caif/linux_caif.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/caif/linux_caif.rst b/Documentation/networking/caif/linux_caif.rst index a0480862ab8ce..969e4a2af98f5 100644 --- a/Documentation/networking/caif/linux_caif.rst +++ b/Documentation/networking/caif/linux_caif.rst @@ -181,8 +181,8 @@ CAIF Core protocol. The IP Interface and CAIF socket have an instance of 'struct cflayer', just like the CAIF Core protocol stack. Net device and Socket implement the 'receive()' function defined by 'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and -receive of packets is handled as by the rest of the layers: the 'dn->transmit()' -function is called in order to transmit data. +receive of packets is handled like the rest of the layers: the 'dn->transmit()' +function is called to transmit data. Configuration of Link Layer --------------------------- From 4705f36bc2b765ee8b90e9bf913200ebfa283bc5 Mon Sep 17 00:00:00 2001 From: Daniel Zahka Date: Tue, 16 Dec 2025 06:21:35 -0800 Subject: [PATCH 124/214] selftests: drv-net: psp: fix templated test names in psp_ip_ver_test_builder() test_case will only take on its formatted name after it is called by the test runner. Move the assignment to test_case.__name__ to when the test_case is constructed, not called. Fixes: 8f90dc6e417a ("selftests: drv-net: psp: add basic data transfer and key rotation tests") Signed-off-by: Daniel Zahka Signed-off-by: NipaLocal --- tools/testing/selftests/drivers/net/psp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py index 06559ef49b9a5..56dee824bb4cc 100755 --- a/tools/testing/selftests/drivers/net/psp.py +++ b/tools/testing/selftests/drivers/net/psp.py @@ -573,8 +573,9 @@ def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver): """Build test cases for each combo of PSP version and IP version""" def test_case(cfg): cfg.require_ipver(ipver) - test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}" test_func(cfg, psp_ver, ipver) + + test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}" return test_case From 38e2874759cc7c44082c552f485f83ec39114d2c Mon Sep 17 00:00:00 2001 From: Daniel Zahka Date: Tue, 16 Dec 2025 06:21:36 -0800 Subject: [PATCH 125/214] selftests: drv-net: psp: fix test names in ipver_test_builder() test_case will only take on the formatted name after being called. This does not work with the way ksft_run() currently works. Assign the name after the test_case is created. Fixes: 81236c74dba6 ("selftests: drv-net: psp: add test for auto-adjusting TCP MSS") Signed-off-by: Daniel Zahka Signed-off-by: NipaLocal --- tools/testing/selftests/drivers/net/psp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py index 56dee824bb4cc..52523bdad2407 100755 --- a/tools/testing/selftests/drivers/net/psp.py +++ b/tools/testing/selftests/drivers/net/psp.py @@ -583,8 +583,9 @@ def ipver_test_builder(name, test_func, ipver): """Build test cases for each IP version""" def test_case(cfg): cfg.require_ipver(ipver) - test_case.__name__ = f"{name}_ip{ipver}" test_func(cfg, ipver) + + test_case.__name__ = f"{name}_ip{ipver}" return test_case From 44647e70a8cb89dec9a43cf8e78f50226793b5df Mon Sep 17 00:00:00 2001 From: Deepakkumar Karn Date: Tue, 16 Dec 2025 20:43:05 +0530 Subject: [PATCH 126/214] net: usb: rtl8150: fix memory leak on usb_submit_urb() failure In async_set_registers(), when usb_submit_urb() fails, the allocated async_req structure and URB are not freed, causing a memory leak. The completion callback async_set_reg_cb() is responsible for freeing these allocations, but it is only called after the URB is successfully submitted and completes (successfully or with error). If submission fails, the callback never runs and the memory is leaked. Fix this by freeing both the URB and the request structure in the error path when usb_submit_urb() fails. Reported-by: syzbot+8dd915c7cb0490fc8c52@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8dd915c7cb0490fc8c52 Fixes: 4d12997a9bb3 ("drivers: net: usb: rtl8150: concurrent URB bugfix") Signed-off-by: Deepakkumar Karn Signed-off-by: NipaLocal --- drivers/net/usb/rtl8150.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 278e6cb6f4d99..e40b0669d9f4b 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -211,6 +211,8 @@ static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg) if (res == -ENODEV) netif_device_detach(dev->netdev); dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res); + kfree(req); + usb_free_urb(async_urb); } return res; } From cef537db9cb13541838fb2abb4a56584ec01d762 Mon Sep 17 00:00:00 2001 From: Stefano Radaelli Date: Tue, 16 Dec 2025 17:25:34 +0100 Subject: [PATCH 127/214] net: phy: mxl-86110: Add power management and soft reset support Implement soft_reset, suspend, and resume callbacks using genphy_soft_reset(), genphy_suspend(), and genphy_resume() to fix PHY initialization and power management issues. The soft_reset callback is needed to properly recover the PHY after an ifconfig down/up cycle. Without it, the PHY can remain in power-down state, causing MDIO register access failures during config_init(). The soft reset ensures the PHY is operational before configuration. The suspend/resume callbacks enable proper power management during system suspend/resume cycles. Fixes: b2908a989c59 ("net: phy: add driver for MaxLinear MxL86110 PHY") Signed-off-by: Stefano Radaelli Signed-off-by: NipaLocal --- drivers/net/phy/mxl-86110.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/phy/mxl-86110.c b/drivers/net/phy/mxl-86110.c index e5d137a37a1d4..42a5fe3f115f4 100644 --- a/drivers/net/phy/mxl-86110.c +++ b/drivers/net/phy/mxl-86110.c @@ -938,6 +938,9 @@ static struct phy_driver mxl_phy_drvs[] = { PHY_ID_MATCH_EXACT(PHY_ID_MXL86110), .name = "MXL86110 Gigabit Ethernet", .config_init = mxl86110_config_init, + .suspend = genphy_suspend, + .resume = genphy_resume, + .soft_reset = genphy_soft_reset, .get_wol = mxl86110_get_wol, .set_wol = mxl86110_set_wol, .led_brightness_set = mxl86110_led_brightness_set, From 8dd39f6a9e71db8a319e4e26834d392884ac9bcf Mon Sep 17 00:00:00 2001 From: "Alice C. Munduruca" Date: Tue, 16 Dec 2025 12:06:41 -0500 Subject: [PATCH 128/214] selftests: net: fix "buffer overflow detected" for tap.c When the selftest 'tap.c' is compiled with '-D_FORTIFY_SOURCE=3', the strcpy() in rtattr_add_strsz() is replaced with a checked version which causes the test to consistently fail when compiled with toolchains for which this option is enabled by default. TAP version 13 1..3 # Starting 3 tests from 1 test cases. # RUN tap.test_packet_valid_udp_gso ... *** buffer overflow detected ***: terminated # test_packet_valid_udp_gso: Test terminated by assertion # FAIL tap.test_packet_valid_udp_gso not ok 1 tap.test_packet_valid_udp_gso # RUN tap.test_packet_valid_udp_csum ... *** buffer overflow detected ***: terminated # test_packet_valid_udp_csum: Test terminated by assertion # FAIL tap.test_packet_valid_udp_csum not ok 2 tap.test_packet_valid_udp_csum # RUN tap.test_packet_crash_tap_invalid_eth_proto ... *** buffer overflow detected ***: terminated # test_packet_crash_tap_invalid_eth_proto: Test terminated by assertion # FAIL tap.test_packet_crash_tap_invalid_eth_proto not ok 3 tap.test_packet_crash_tap_invalid_eth_proto # FAILED: 0 / 3 tests passed. # Totals: pass:0 fail:3 xfail:0 xpass:0 skip:0 error:0 A buffer overflow is detected by the fortified glibc __strcpy_chk() since the __builtin_object_size() of `RTA_DATA(rta)` is incorrectly reported as 1, even though there is ample space in its bounding buffer `req`. Additionally, given that IFLA_IFNAME also expects a null-terminated string, callers of rtaddr_add_str{,sz}() could simply use the rtaddr_add_strsz() variant. (which has been renamed to remove the trailing `sz`) memset() has been used for this function since it is unchecked and thus circumvents the issue discussed in the previous paragraph. Fixes: 2e64fe4624d1 ("selftests: add few test cases for tap driver") Signed-off-by: Alice C. Munduruca Reviewed-by: Cengiz Can Reviewed-by: Willem de Bruijn Signed-off-by: NipaLocal --- tools/testing/selftests/net/tap.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/net/tap.c b/tools/testing/selftests/net/tap.c index 9ec1c9b50e772..a0c9418132c82 100644 --- a/tools/testing/selftests/net/tap.c +++ b/tools/testing/selftests/net/tap.c @@ -56,18 +56,12 @@ static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, const char *s) { - struct rtattr *rta = rtattr_add(nh, type, strlen(s)); + unsigned int strsz = strlen(s) + 1; + struct rtattr *rta; - memcpy(RTA_DATA(rta), s, strlen(s)); - return rta; -} - -static struct rtattr *rtattr_add_strsz(struct nlmsghdr *nh, unsigned short type, - const char *s) -{ - struct rtattr *rta = rtattr_add(nh, type, strlen(s) + 1); + rta = rtattr_add(nh, type, strsz); - strcpy(RTA_DATA(rta), s); + memcpy(RTA_DATA(rta), s, strsz); return rta; } @@ -119,7 +113,7 @@ static int dev_create(const char *dev, const char *link_type, link_info = rtattr_begin(&req.nh, IFLA_LINKINFO); - rtattr_add_strsz(&req.nh, IFLA_INFO_KIND, link_type); + rtattr_add_str(&req.nh, IFLA_INFO_KIND, link_type); if (fill_info_data) { info_data = rtattr_begin(&req.nh, IFLA_INFO_DATA); From 5388b91897b6a82a60570c6bc68bed3faa83a1a6 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Tue, 16 Dec 2025 20:41:12 +0200 Subject: [PATCH 129/214] net: usb: pegasus: fix memory leak on usb_submit_urb() failure In update_eth_regs_async() neither the URB nor the request structure are being freed if usb_submit_urb() fails. The patch fixes this long lurking bug in the error path. Signed-off-by: Petko Manolov Signed-off-by: NipaLocal --- drivers/net/usb/pegasus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 81ca64debc5b9..7a70207e7364e 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -168,6 +168,8 @@ static int update_eth_regs_async(pegasus_t *pegasus) netif_device_detach(pegasus->net); netif_err(pegasus, drv, pegasus->net, "%s returned %d\n", __func__, ret); + kfree(req); + usb_free_urb(async_urb); } return ret; } From 6e8d9294754f0b66028cb805e1a0091087f583df Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 Dec 2025 22:35:42 +0100 Subject: [PATCH 130/214] net: wangxun: move PHYLINK dependency The LIBWX library code is what calls into phylink, so any user of it has to select CONFIG_PHYLINK at the moment, with NGBEVF missing this: x86_64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_ethtool.o: in function `wx_nway_reset': wx_ethtool.c:(.text+0x613): undefined reference to `phylink_ethtool_nway_reset' x86_64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_ethtool.o: in function `wx_get_link_ksettings': wx_ethtool.c:(.text+0x62b): undefined reference to `phylink_ethtool_ksettings_get' x86_64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_ethtool.o: in function `wx_set_link_ksettings': wx_ethtool.c:(.text+0x643): undefined reference to `phylink_ethtool_ksettings_set' x86_64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_ethtool.o: in function `wx_get_pauseparam': wx_ethtool.c:(.text+0x65b): undefined reference to `phylink_ethtool_get_pauseparam' x86_64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_ethtool.o: in function `wx_set_pauseparam': wx_ethtool.c:(.text+0x677): undefined reference to `phylink_ethtool_set_pauseparam' Add the 'select PHYLINK' line in the libwx option directly so this will always be enabled for all current and future wangxun drivers, and remove the now duplicate lines. Fixes: a0008a3658a3 ("net: wangxun: add ngbevf build") Signed-off-by: Arnd Bergmann Reviewed-by: Vadim Fedorenko Signed-off-by: NipaLocal --- drivers/net/ethernet/wangxun/Kconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index d138dea7d208d..ec278f99d2955 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -21,6 +21,7 @@ config LIBWX depends on PTP_1588_CLOCK_OPTIONAL select PAGE_POOL select DIMLIB + select PHYLINK help Common library for Wangxun(R) Ethernet drivers. @@ -29,7 +30,6 @@ config NGBE depends on PCI depends on PTP_1588_CLOCK_OPTIONAL select LIBWX - select PHYLINK help This driver supports Wangxun(R) GbE PCI Express family of adapters. @@ -48,7 +48,6 @@ config TXGBE depends on PTP_1588_CLOCK_OPTIONAL select MARVELL_10G_PHY select REGMAP - select PHYLINK select HWMON if TXGBE=y select SFP select GPIOLIB @@ -71,7 +70,6 @@ config TXGBEVF depends on PCI_MSI depends on PTP_1588_CLOCK_OPTIONAL select LIBWX - select PHYLINK help This driver supports virtual functions for SP1000A, WX1820AL, WX5XXX, WX5XXXAL. From 2da5547b0b10180d39bb502c4cfa5cf95ddece82 Mon Sep 17 00:00:00 2001 From: Jibin Zhang Date: Wed, 17 Dec 2025 11:55:42 +0800 Subject: [PATCH 131/214] net: fix segmentation of forwarding fraglist GRO This patch enhances GSO segment checks by verifying the presence of frag_list and protocol consistency, addressing low throughput issues on IPv4 servers when used as hotspots Specifically, it fixes a bug in GSO segmentation when forwarding GRO packets with frag_list. The function skb_segment_list cannot correctly process GRO skbs converted by XLAT, because XLAT only converts the header of the head skb. As a result, skbs in the frag_list may remain unconverted, leading to protocol inconsistencies and reduced throughput. To resolve this, the patch uses skb_segment to handle forwarded packets converted by XLAT, ensuring that all fragments are properly converted and segmented. Signed-off-by: Jibin Zhang Signed-off-by: NipaLocal --- net/ipv4/tcp_offload.c | 3 ++- net/ipv4/udp_offload.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index fdda18b1abda0..162a384a15bbe 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -104,7 +104,8 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, if (!pskb_may_pull(skb, sizeof(struct tcphdr))) return ERR_PTR(-EINVAL); - if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) { + if ((skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) && skb_has_frag_list(skb) && + (skb->protocol == skb_shinfo(skb)->frag_list->protocol)) { struct tcphdr *th = tcp_hdr(skb); if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size) diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 19d0b5b09ffae..704fb32d10d75 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -512,7 +512,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, return NULL; } - if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) { + if ((skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) && skb_has_frag_list(gso_skb) && + (gso_skb->protocol == skb_shinfo(gso_skb)->frag_list->protocol)) { /* Detect modified geometry and pass those to skb_segment. */ if (skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size) return __udp_gso_segment_list(gso_skb, features, is_ipv6); From 6da54e300e22479da9043de398ec4a75ff486f7a Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Wed, 17 Dec 2025 00:14:18 -0700 Subject: [PATCH 132/214] ptp: prevent info leak to userspace Somehow the `memset` is lost after refactor, which leaks a lot of kernel stack data to userspace directly. This patch clears the reserved data region to prevent the info leak. Signed-off-by: Kyle Zeng Signed-off-by: NipaLocal --- drivers/ptp/ptp_chardev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index c61cf9edac48b..06f71011fb041 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -195,6 +195,8 @@ static long ptp_clock_getcaps(struct ptp_clock *ptp, void __user *arg) if (caps.adjust_phase) caps.max_phase_adj = ptp->info->getmaxphase(ptp->info); + memset(caps.rsv, 0, sizeof(caps.rsv)); + return copy_to_user(arg, &caps, sizeof(caps)) ? -EFAULT : 0; } From 3bfdf5e9dad90ee4dc13833e23c049c2287e8150 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Wed, 17 Dec 2025 08:51:15 +0000 Subject: [PATCH 133/214] smc91x: fix broken irq-context in PREEMPT_RT When smc91x.c is built with PREEMPT_RT, the following splat occurs in FVP_RevC: [ 13.055000] smc91x LNRO0003:00 eth0: link up, 10Mbps, half-duplex, lpa 0x0000 [ 13.062137] BUG: workqueue leaked atomic, lock or RCU: kworker/2:1[106] [ 13.062137] preempt=0x00000000 lock=0->0 RCU=0->1 workfn=mld_ifc_work [ 13.062266] C ** replaying previous printk message ** [ 13.062266] CPU: 2 UID: 0 PID: 106 Comm: kworker/2:1 Not tainted 6.18.0-dirty #179 PREEMPT_{RT,(full)} [ 13.062353] Hardware name: , BIOS [ 13.062382] Workqueue: mld mld_ifc_work [ 13.062469] Call trace: [ 13.062494] show_stack+0x24/0x40 (C) [ 13.062602] __dump_stack+0x28/0x48 [ 13.062710] dump_stack_lvl+0x7c/0xb0 [ 13.062818] dump_stack+0x18/0x34 [ 13.062926] process_scheduled_works+0x294/0x450 [ 13.063043] worker_thread+0x260/0x3d8 [ 13.063124] kthread+0x1c4/0x228 [ 13.063235] ret_from_fork+0x10/0x20 This happens because smc_special_trylock() disables IRQs even on PREEMPT_RT, but smc_special_unlock() does not restore IRQs on PREEMPT_RT. The reason is that smc_special_unlock() calls spin_unlock_irqrestore(), and rcu_read_unlock_bh() in __dev_queue_xmit() cannot invoke rcu_read_unlock() through __local_bh_enable_ip() when current->softirq_disable_cnt becomes zero. To address this issue, replace smc_special_trylock() with spin_trylock_irqsave(). Fixes: 342a93247e08 ("locking/spinlock: Provide RT variant header: ") Signed-off-by: Yeoreum Yun Reviewed-by: Simon Horman Signed-off-by: NipaLocal --- drivers/net/ethernet/smsc/smc91x.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 9d1a83a5fa7e5..d16c178d10344 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -516,15 +516,7 @@ static inline void smc_rcv(struct net_device *dev) * any other concurrent access and C would always interrupt B. But life * isn't that easy in a SMP world... */ -#define smc_special_trylock(lock, flags) \ -({ \ - int __ret; \ - local_irq_save(flags); \ - __ret = spin_trylock(lock); \ - if (!__ret) \ - local_irq_restore(flags); \ - __ret; \ -}) +#define smc_special_trylock(lock, flags) spin_trylock_irqsave(lock, flags) #define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags) #define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags) #else From d522b3ebbc956d895dc7f32239fc7fa3f0a177a3 Mon Sep 17 00:00:00 2001 From: Rajashekar Hudumula Date: Wed, 17 Dec 2025 02:47:48 -0800 Subject: [PATCH 134/214] bng_en: update module description The Broadcom BCM57708/800G NIC family is branded as ThorUltra. Update the driver description accordingly. Fixes: 74715c4ab0fa0 ("bng_en: Add PCI interface") Signed-off-by: Rajashekar Hudumula Reviewed-by: Vikas Gupta Reviewed-by: Bhargava Chenna Marreddy Signed-off-by: NipaLocal --- drivers/net/ethernet/broadcom/Kconfig | 8 ++++---- drivers/net/ethernet/broadcom/bnge/bnge.h | 2 +- drivers/net/ethernet/broadcom/bnge/bnge_core.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 666522d647751..ca565ace6e6ad 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -255,14 +255,14 @@ config BNXT_HWMON devices, via the hwmon sysfs interface. config BNGE - tristate "Broadcom Ethernet device support" + tristate "Broadcom ThorUltra Ethernet device support" depends on PCI select NET_DEVLINK select PAGE_POOL help - This driver supports Broadcom 50/100/200/400/800 gigabit Ethernet cards. - The module will be called bng_en. To compile this driver as a module, - choose M here. + This driver supports Broadcom ThorUltra 50/100/200/400/800 gigabit + Ethernet cards. The module will be called bng_en. To compile this + driver as a module, choose M here. config BCMASP tristate "Broadcom ASP 2.0 Ethernet support" diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h index 411744894349f..32fc16a37d02a 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge.h @@ -5,7 +5,7 @@ #define _BNGE_H_ #define DRV_NAME "bng_en" -#define DRV_SUMMARY "Broadcom 800G Ethernet Linux Driver" +#define DRV_SUMMARY "Broadcom ThorUltra NIC Ethernet Driver" #include #include diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c index c94e132bebc80..b4090283df0f2 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c @@ -19,7 +19,7 @@ char bnge_driver_name[] = DRV_NAME; static const struct { char *name; } board_info[] = { - [BCM57708] = { "Broadcom BCM57708 50Gb/100Gb/200Gb/400Gb/800Gb Ethernet" }, + [BCM57708] = { "Broadcom BCM57708 ThorUltra 50Gb/100Gb/200Gb/400Gb/800Gb Ethernet" }, }; static const struct pci_device_id bnge_pci_tbl[] = { From ee8e02f95b5a255e4df8714ef16391f0fd0efdc0 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Wed, 17 Dec 2025 12:48:19 +0100 Subject: [PATCH 135/214] net/smc: Initialize smc hashtables before registering users During initialisation of the SMC module initialize smc_v4/6_hashinfo before calling smc_nl_init(), proto_register() or sock_register(), to avoid a race that can cause use of an uninitialised pointer in case an smc protocol is called before the module is done initialising. syzbot report: KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] Call Trace: smc_diag_dump+0x59/0xa0 net/smc/smc_diag.c:236 netlink_dump+0x647/0xd80 net/netlink/af_netlink.c:2325 __netlink_dump_start+0x59f/0x780 net/netlink/af_netlink.c:2440 netlink_dump_start include/linux/netlink.h:339 [inline] smc_diag_handler_dump+0x1ab/0x250 net/smc/smc_diag.c:251 sock_diag_rcv_msg+0x3dc/0x5f0 netlink_rcv_skb+0x1e3/0x430 net/netlink/af_netlink.c:2550 netlink_unicast_kernel net/netlink/af_netlink.c:1331 [inline] netlink_unicast+0x7f0/0x990 net/netlink/af_netlink.c:1357 netlink_sendmsg+0x8e4/0xcb0 net/netlink/af_netlink.c:1901 Fixes: f16a7dd5cf27 ("smc: netlink interface for SMC sockets") Reported-by: syzbot+f69bfae0a4eb29976e44@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f69bfae0a4eb29976e44 Signed-off-by: Alexandra Winter Signed-off-by: NipaLocal --- net/smc/af_smc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index f97f77b041d97..b0f4405fb714c 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -3524,6 +3524,9 @@ static int __init smc_init(void) goto out_pernet_subsys_stat; smc_clc_init(); + INIT_HLIST_HEAD(&smc_v4_hashinfo.ht); + INIT_HLIST_HEAD(&smc_v6_hashinfo.ht); + rc = smc_nl_init(); if (rc) goto out_ism; @@ -3581,8 +3584,6 @@ static int __init smc_init(void) pr_err("%s: sock_register fails with %d\n", __func__, rc); goto out_proto6; } - INIT_HLIST_HEAD(&smc_v4_hashinfo.ht); - INIT_HLIST_HEAD(&smc_v6_hashinfo.ht); rc = smc_ib_register_client(); if (rc) { From 57d74c5838f0907ea113822099eb0cd09c796806 Mon Sep 17 00:00:00 2001 From: Przemyslaw Korba Date: Wed, 17 Dec 2025 11:49:40 -0800 Subject: [PATCH 136/214] i40e: fix scheduling in set_rx_mode Add service task schedule to set_rx_mode. In some cases there are error messages printed out in PTP application (ptp4l): ptp4l[13848.762]: port 1 (ens2f3np3): received SYNC without timestamp ptp4l[13848.825]: port 1 (ens2f3np3): received SYNC without timestamp ptp4l[13848.887]: port 1 (ens2f3np3): received SYNC without timestamp This happens when service task would not run immediately after set_rx_mode, and we need it for setup tasks. This service task checks, if PTP RX packets are hung in firmware, and propagate correct settings such as multicast address for IEEE 1588 Precision Time Protocol. RX timestamping depends on some of these filters set. Bug happens only with high PTP packets frequency incoming, and not every run since sometimes service task is being ran from a different place immediately after starting ptp4l. Fixes: 0e4425ed641f ("i40e: fix: do not sleep in netdev_ops") Reviewed-by: Grzegorz Nitka Reviewed-by: Jacob Keller Reviewed-by: Aleksandr Loktionov Signed-off-by: Przemyslaw Korba Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/i40e/i40e_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d8192aa232548..0b1cc0481027a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2234,6 +2234,7 @@ static void i40e_set_rx_mode(struct net_device *netdev) vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state); } + i40e_service_event_schedule(vsi->back); } /** From 1ae95b439ada40831313a4d33b696a653090c6ce Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 17 Dec 2025 11:49:41 -0800 Subject: [PATCH 137/214] i40e: validate ring_len parameter against hardware-specific values The maximum number of descriptors supported by the hardware is hardware-dependent and can be retrieved using i40e_get_max_num_descriptors(). Move this function to a shared header and use it when checking for valid ring_len parameter rather than using hardcoded value. By fixing an over-acceptance issue, behavior change could be seen where ring_len could now be rejected while configuring rx and tx queues if its size is larger than the hardware-dependent maximum number of descriptors. Fixes: 55d225670def ("i40e: add validation for ring_len param") Signed-off-by: Gregory Herrero Tested-by: Rafal Romanowski Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Reviewed-by: Brett Creeley Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/i40e/i40e.h | 11 +++++++++++ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 12 ------------ drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d2d03db2acec6..dcb50c2e1aa27 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -1422,4 +1422,15 @@ static inline struct i40e_veb *i40e_pf_get_main_veb(struct i40e_pf *pf) return (pf->lan_veb != I40E_NO_VEB) ? pf->veb[pf->lan_veb] : NULL; } +static inline u32 i40e_get_max_num_descriptors(const struct i40e_pf *pf) +{ + const struct i40e_hw *hw = &pf->hw; + + switch (hw->mac.type) { + case I40E_MAC_XL710: + return I40E_MAX_NUM_DESCRIPTORS_XL710; + default: + return I40E_MAX_NUM_DESCRIPTORS; + } +} #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f2c2646ea2989..6a47ea0927e96 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2013,18 +2013,6 @@ static void i40e_get_drvinfo(struct net_device *netdev, drvinfo->n_priv_flags += I40E_GL_PRIV_FLAGS_STR_LEN; } -static u32 i40e_get_max_num_descriptors(struct i40e_pf *pf) -{ - struct i40e_hw *hw = &pf->hw; - - switch (hw->mac.type) { - case I40E_MAC_XL710: - return I40E_MAX_NUM_DESCRIPTORS_XL710; - default: - return I40E_MAX_NUM_DESCRIPTORS; - } -} - static void i40e_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 8b30a3accd310..1fa877b52f618 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -656,7 +656,7 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_id, /* ring_len has to be multiple of 8 */ if (!IS_ALIGNED(info->ring_len, 8) || - info->ring_len > I40E_MAX_NUM_DESCRIPTORS_XL710) { + info->ring_len > i40e_get_max_num_descriptors(pf)) { ret = -EINVAL; goto error_context; } @@ -726,7 +726,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id, /* ring_len has to be multiple of 32 */ if (!IS_ALIGNED(info->ring_len, 32) || - info->ring_len > I40E_MAX_NUM_DESCRIPTORS_XL710) { + info->ring_len > i40e_get_max_num_descriptors(pf)) { ret = -EINVAL; goto error_param; } From ebc08739a227f40bc7d56fc0576202ef65318a0a Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Wed, 17 Dec 2025 11:49:42 -0800 Subject: [PATCH 138/214] iavf: fix off-by-one issues in iavf_config_rss_reg() There are off-by-one bugs when configuring RSS hash key and lookup table, causing out-of-bounds reads to memory [1] and out-of-bounds writes to device registers. Before commit 43a3d9ba34c9 ("i40evf: Allow PF driver to configure RSS"), the loop upper bounds were: i <= I40E_VFQF_{HKEY,HLUT}_MAX_INDEX which is safe since the value is the last valid index. That commit changed the bounds to: i <= adapter->rss_{key,lut}_size / 4 where `rss_{key,lut}_size / 4` is the number of dwords, so the last valid index is `(rss_{key,lut}_size / 4) - 1`. Therefore, using `<=` accesses one element past the end. Fix the issues by using `<` instead of `<=`, ensuring we do not exceed the bounds. [1] KASAN splat about rss_key_size off-by-one BUG: KASAN: slab-out-of-bounds in iavf_config_rss+0x619/0x800 Read of size 4 at addr ffff888102c50134 by task kworker/u8:6/63 CPU: 0 UID: 0 PID: 63 Comm: kworker/u8:6 Not tainted 6.18.0-rc2-enjuk-tnguy-00378-g3005f5b77652-dirty #156 PREEMPT(voluntary) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: iavf iavf_watchdog_task Call Trace: dump_stack_lvl+0x6f/0xb0 print_report+0x170/0x4f3 kasan_report+0xe1/0x1a0 iavf_config_rss+0x619/0x800 iavf_watchdog_task+0x2be7/0x3230 process_one_work+0x7fd/0x1420 worker_thread+0x4d1/0xd40 kthread+0x344/0x660 ret_from_fork+0x249/0x320 ret_from_fork_asm+0x1a/0x30 Allocated by task 63: kasan_save_stack+0x30/0x50 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x7f/0x90 __kmalloc_noprof+0x246/0x6f0 iavf_watchdog_task+0x28fc/0x3230 process_one_work+0x7fd/0x1420 worker_thread+0x4d1/0xd40 kthread+0x344/0x660 ret_from_fork+0x249/0x320 ret_from_fork_asm+0x1a/0x30 The buggy address belongs to the object at ffff888102c50100 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 0 bytes to the right of allocated 52-byte region [ffff888102c50100, ffff888102c50134) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x102c50 flags: 0x200000000000000(node=0|zone=2) page_type: f5(slab) raw: 0200000000000000 ffff8881000418c0 dead000000000122 0000000000000000 raw: 0000000000000000 0000000080200020 00000000f5000000 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff888102c50000: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc ffff888102c50080: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc >ffff888102c50100: 00 00 00 00 00 00 04 fc fc fc fc fc fc fc fc fc ^ ffff888102c50180: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc ffff888102c50200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc Fixes: 43a3d9ba34c9 ("i40evf: Allow PF driver to configure RSS") Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemek Kitszel Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/iavf/iavf_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index c2fbe443ef853..4b0fc8f354bc9 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -1726,11 +1726,11 @@ static int iavf_config_rss_reg(struct iavf_adapter *adapter) u16 i; dw = (u32 *)adapter->rss_key; - for (i = 0; i <= adapter->rss_key_size / 4; i++) + for (i = 0; i < adapter->rss_key_size / 4; i++) wr32(hw, IAVF_VFQF_HKEY(i), dw[i]); dw = (u32 *)adapter->rss_lut; - for (i = 0; i <= adapter->rss_lut_size / 4; i++) + for (i = 0; i < adapter->rss_lut_size / 4; i++) wr32(hw, IAVF_VFQF_HLUT(i), dw[i]); iavf_flush(hw); From 5681f433d2574678dc9183e6d7e792f7c2a2cd09 Mon Sep 17 00:00:00 2001 From: Larysa Zaremba Date: Wed, 17 Dec 2025 11:49:43 -0800 Subject: [PATCH 139/214] idpf: fix LAN memory regions command on some NVMs IPU SDK versions 1.9 through 2.0.5 require send buffer to contain a single empty memory region. Set number of regions to 1 and use appropriate send buffer size to satisfy this requirement. Fixes: 6aa53e861c1a ("idpf: implement get LAN MMIO memory regions") Suggested-by: Michal Swiatkowski Reviewed-by: Aleksandr Loktionov Signed-off-by: Larysa Zaremba Tested-by: Krishneil Singh Signed-off-by: Tony Nguyen Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index 44cd4b466c481..5bbe7d9294c14 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -1016,6 +1016,9 @@ static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter) struct idpf_vc_xn_params xn_params = { .vc_op = VIRTCHNL2_OP_GET_LAN_MEMORY_REGIONS, .recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN, + .send_buf.iov_len = + sizeof(struct virtchnl2_get_lan_memory_regions) + + sizeof(struct virtchnl2_mem_region), .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC, }; int num_regions, size; @@ -1028,6 +1031,8 @@ static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter) return -ENOMEM; xn_params.recv_buf.iov_base = rcvd_regions; + rcvd_regions->num_memory_regions = cpu_to_le16(1); + xn_params.send_buf.iov_base = rcvd_regions; reply_sz = idpf_vc_xn_exec(adapter, &xn_params); if (reply_sz < 0) return reply_sz; From b2d63a9def52e1258b08798b3aefcd0af0dedfc7 Mon Sep 17 00:00:00 2001 From: Brian Vazquez Date: Wed, 17 Dec 2025 11:49:44 -0800 Subject: [PATCH 140/214] idpf: reduce mbx_task schedule delay to 300us During the IDPF init phase, the mailbox runs in poll mode until it is configured to properly handle interrupts. The previous delay of 300ms is excessively long for the mailbox polling mechanism, which causes a slow initialization of ~2s: echo 0000:06:12.4 > /sys/bus/pci/drivers/idpf/bind [ 52.444239] idpf 0000:06:12.4: enabling device (0000 -> 0002) [ 52.485005] idpf 0000:06:12.4: Device HW Reset initiated [ 54.177181] idpf 0000:06:12.4: PTP init failed, err=-EOPNOTSUPP [ 54.206177] idpf 0000:06:12.4: Minimum RX descriptor support not provided, using the default [ 54.206182] idpf 0000:06:12.4: Minimum TX descriptor support not provided, using the default Changing the delay to 300us avoids the delays during the initial mailbox transactions, making the init phase much faster: [ 83.342590] idpf 0000:06:12.4: enabling device (0000 -> 0002) [ 83.384402] idpf 0000:06:12.4: Device HW Reset initiated [ 83.518323] idpf 0000:06:12.4: PTP init failed, err=-EOPNOTSUPP [ 83.547430] idpf 0000:06:12.4: Minimum RX descriptor support not provided, using the default [ 83.547435] idpf 0000:06:12.4: Minimum TX descriptor support not provided, using the default Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") Signed-off-by: Brian Vazquez Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index 7a7e101afeb68..7ce4eb71a433c 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -1271,7 +1271,7 @@ void idpf_mbx_task(struct work_struct *work) idpf_mb_irq_enable(adapter); else queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, - msecs_to_jiffies(300)); + usecs_to_jiffies(300)); idpf_recv_mb_msg(adapter); } From 169f5bbbb8a8c276064e1b82924f8af207ede172 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 17 Dec 2025 11:49:45 -0800 Subject: [PATCH 141/214] e1000: fix OOB in e1000_tbi_should_accept() In e1000_tbi_should_accept() we read the last byte of the frame via 'data[length - 1]' to evaluate the TBI workaround. If the descriptor- reported length is zero or larger than the actual RX buffer size, this read goes out of bounds and can hit unrelated slab objects. The issue is observed from the NAPI receive path (e1000_clean_rx_irq): ================================================================== BUG: KASAN: slab-out-of-bounds in e1000_tbi_should_accept+0x610/0x790 Read of size 1 at addr ffff888014114e54 by task sshd/363 CPU: 0 PID: 363 Comm: sshd Not tainted 5.18.0-rc1 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x5a/0x74 print_address_description+0x7b/0x440 print_report+0x101/0x200 kasan_report+0xc1/0xf0 e1000_tbi_should_accept+0x610/0x790 e1000_clean_rx_irq+0xa8c/0x1110 e1000_clean+0xde2/0x3c10 __napi_poll+0x98/0x380 net_rx_action+0x491/0xa20 __do_softirq+0x2c9/0x61d do_softirq+0xd1/0x120 __local_bh_enable_ip+0xfe/0x130 ip_finish_output2+0x7d5/0xb00 __ip_queue_xmit+0xe24/0x1ab0 __tcp_transmit_skb+0x1bcb/0x3340 tcp_write_xmit+0x175d/0x6bd0 __tcp_push_pending_frames+0x7b/0x280 tcp_sendmsg_locked+0x2e4f/0x32d0 tcp_sendmsg+0x24/0x40 sock_write_iter+0x322/0x430 vfs_write+0x56c/0xa60 ksys_write+0xd1/0x190 do_syscall_64+0x43/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae RIP: 0033:0x7f511b476b10 Code: 73 01 c3 48 8b 0d 88 d3 2b 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 83 3d f9 2b 2c 00 00 75 10 b8 01 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 8e 9b 01 00 48 89 04 24 RSP: 002b:00007ffc9211d4e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000000000004024 RCX: 00007f511b476b10 RDX: 0000000000004024 RSI: 0000559a9385962c RDI: 0000000000000003 RBP: 0000559a9383a400 R08: fffffffffffffff0 R09: 0000000000004f00 R10: 0000000000000070 R11: 0000000000000246 R12: 0000000000000000 R13: 00007ffc9211d57f R14: 0000559a9347bde7 R15: 0000000000000003 Allocated by task 1: __kasan_krealloc+0x131/0x1c0 krealloc+0x90/0xc0 add_sysfs_param+0xcb/0x8a0 kernel_add_sysfs_param+0x81/0xd4 param_sysfs_builtin+0x138/0x1a6 param_sysfs_init+0x57/0x5b do_one_initcall+0x104/0x250 do_initcall_level+0x102/0x132 do_initcalls+0x46/0x74 kernel_init_freeable+0x28f/0x393 kernel_init+0x14/0x1a0 ret_from_fork+0x22/0x30 The buggy address belongs to the object at ffff888014114000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1620 bytes to the right of 2048-byte region [ffff888014114000, ffff888014114800] The buggy address belongs to the physical page: page:ffffea0000504400 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x14110 head:ffffea0000504400 order:3 compound_mapcount:0 compound_pincount:0 flags: 0x100000000010200(slab|head|node=0|zone=1) raw: 0100000000010200 0000000000000000 dead000000000001 ffff888013442000 raw: 0000000000000000 0000000000080008 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected ================================================================== This happens because the TBI check unconditionally dereferences the last byte without validating the reported length first: u8 last_byte = *(data + length - 1); Fix by rejecting the frame early if the length is zero, or if it exceeds adapter->rx_buffer_len. This preserves the TBI workaround semantics for valid frames and prevents touching memory beyond the RX buffer. Fixes: 2037110c96d5 ("e1000: move tbi workaround code into helper function") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Simon Horman Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/e1000/e1000_main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 292389aceb2d4..7f078ec9c14c5 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -4094,7 +4094,15 @@ static bool e1000_tbi_should_accept(struct e1000_adapter *adapter, u32 length, const u8 *data) { struct e1000_hw *hw = &adapter->hw; - u8 last_byte = *(data + length - 1); + u8 last_byte; + + /* Guard against OOB on data[length - 1] */ + if (unlikely(!length)) + return false; + /* Upper bound: length must not exceed rx_buffer_len */ + if (unlikely(length > adapter->rx_buffer_len)) + return false; + last_byte = *(data + length - 1); if (TBI_ACCEPT(hw, status, errors, length, last_byte)) { unsigned long irq_flags; From 1b23f16f557cc85aa8a5e9d1e543031053560438 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Wed, 17 Dec 2025 21:57:56 +0100 Subject: [PATCH 142/214] net: dsa: b53: skip multicast entries for fdb_dump() port_fdb_dump() is supposed to only add fdb entries, but we iterate over the full ARL table, which also inludes multicast entries. So check if the entry is a multicast entry before passing it on to the callback(). Additionally, the port of those entries is a bitmask, not a port number, so any included entries would have even be for the wrong port. Fixes: 1da6df85c6fb ("net: dsa: b53: Implement ARL add/del/dump operations") Signed-off-by: Jonas Gorski Reviewed-by: Florian Fainelli Signed-off-by: NipaLocal --- drivers/net/dsa/b53/b53_common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index a1a177713d99d..2c4131ed7e30b 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2169,6 +2169,9 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, if (!ent->is_valid) return 0; + if (is_multicast_ether_addr(ent->mac)) + return 0; + if (port != ent->port) return 0; From 95f174cafad71dc636243de5b4547f376e51378d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 17 Dec 2025 13:01:53 -0800 Subject: [PATCH 143/214] net: mdio: rtl9300: use scoped for loops Currently in the return path, fwnode_handle_put calls are missing. Just use _scoped to avoid the issue. Fixes: 24e31e474769 ("net: mdio: Add RTL9300 MDIO driver") Signed-off-by: Rosen Penev Signed-off-by: NipaLocal --- drivers/net/mdio/mdio-realtek-rtl9300.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index 33694c3ff9a71..405a07075dd11 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -354,7 +354,6 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri struct fwnode_handle *node) { struct rtl9300_mdio_chan *chan; - struct fwnode_handle *child; struct mii_bus *bus; u32 mdio_bus; int err; @@ -371,7 +370,7 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri * compatible = "ethernet-phy-ieee802.3-c45". This does mean we can't * support both c45 and c22 on the same MDIO bus. */ - fwnode_for_each_child_node(node, child) + fwnode_for_each_child_node_scoped(node, child) if (fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45")) priv->smi_bus_is_c45[mdio_bus] = true; @@ -409,7 +408,6 @@ static int rtl9300_mdiobus_map_ports(struct device *dev) { struct rtl9300_mdio_priv *priv = dev_get_drvdata(dev); struct device *parent = dev->parent; - struct fwnode_handle *port; int err; struct fwnode_handle *ports __free(fwnode_handle) = @@ -418,7 +416,7 @@ static int rtl9300_mdiobus_map_ports(struct device *dev) return dev_err_probe(dev, -EINVAL, "%pfwP missing ethernet-ports\n", dev_fwnode(parent)); - fwnode_for_each_child_node(ports, port) { + fwnode_for_each_child_node_scoped(ports, port) { struct device_node *mdio_dn; u32 addr; u32 bus; From 51aba290ed6f9ffc84b2e91d3859f1935c431625 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Thu, 18 Dec 2025 06:41:56 +0530 Subject: [PATCH 144/214] net: usb: asix: validate PHY address before use The ASIX driver reads the PHY address from the USB device via asix_read_phy_addr(). A malicious or faulty device can return an invalid address (>= PHY_MAX_ADDR), which causes a warning in mdiobus_get_phy(): addr 207 out of range WARNING: drivers/net/phy/mdio_bus.c:76 Validate the PHY address in asix_read_phy_addr() and remove the now-redundant check in ax88172a.c. Reported-by: syzbot+3d43c9066a5b54902232@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3d43c9066a5b54902232 Tested-by: syzbot+3d43c9066a5b54902232@syzkaller.appspotmail.com Fixes: 7e88b11a862a ("net: usb: asix: refactor asix_read_phy_addr() and handle errors on return") Link: https://lore.kernel.org/all/20251217085057.270704-1-kartikey406@gmail.com/T/ [v1] Signed-off-by: Deepanshu Kartikey Reviewed-by: Andrew Lunn Signed-off-by: NipaLocal --- drivers/net/usb/asix_common.c | 5 +++++ drivers/net/usb/ax88172a.c | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 7fd763917ae2c..6ab3486072cb0 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -335,6 +335,11 @@ int asix_read_phy_addr(struct usbnet *dev, bool internal) offset = (internal ? 1 : 0); ret = buf[offset]; + if (ret >= PHY_MAX_ADDR) { + netdev_err(dev->net, "invalid PHY address: %d\n", ret); + return -ENODEV; + } + netdev_dbg(dev->net, "%s PHY address 0x%x\n", internal ? "internal" : "external", ret); diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index f613e4bc68c85..758a423a459b8 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -210,11 +210,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf) ret = asix_read_phy_addr(dev, priv->use_embdphy); if (ret < 0) goto free; - if (ret >= PHY_MAX_ADDR) { - netdev_err(dev->net, "Invalid PHY address %#x\n", ret); - ret = -ENODEV; - goto free; - } + priv->phy_addr = ret; ax88172a_reset_phy(dev, priv->use_embdphy); From 921abfd43891f1110ae37e94d4de2fa9d4a01c5f Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Thu, 18 Dec 2025 06:53:54 +0530 Subject: [PATCH 145/214] net: nfc: fix deadlock between nfc_unregister_device and rfkill_fop_write A deadlock can occur between nfc_unregister_device() and rfkill_fop_write() due to lock ordering inversion between device_lock and rfkill_global_mutex. The problematic lock order is: Thread A (rfkill_fop_write): rfkill_fop_write() mutex_lock(&rfkill_global_mutex) rfkill_set_block() nfc_rfkill_set_block() nfc_dev_down() device_lock(&dev->dev) <- waits for device_lock Thread B (nfc_unregister_device): nfc_unregister_device() device_lock(&dev->dev) rfkill_unregister() mutex_lock(&rfkill_global_mutex) <- waits for rfkill_global_mutex This creates a classic ABBA deadlock scenario. Fix this by moving rfkill_unregister() and rfkill_destroy() outside the device_lock critical section. Store the rfkill pointer in a local variable before releasing the lock, then call rfkill_unregister() after releasing device_lock. This change is safe because rfkill_fop_write() holds rfkill_global_mutex while calling the rfkill callbacks, and rfkill_unregister() also acquires rfkill_global_mutex before cleanup. Therefore, rfkill_unregister() will wait for any ongoing callback to complete before proceeding, and device_del() is only called after rfkill_unregister() returns, preventing any use-after-free. The similar lock ordering in nfc_register_device() (device_lock -> rfkill_global_mutex via rfkill_register) is safe because during registration the device is not yet in rfkill_list, so no concurrent rfkill operations can occur on this device. Fixes: 3e3b5dfcd16a ("NFC: reorder the logic in nfc_{un,}register_device") Cc: stable@vger.kernel.org Reported-by: syzbot+4ef89409a235d804c6c2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=4ef89409a235d804c6c2 Link: https://lore.kernel.org/all/20251217054908.178907-1-kartikey406@gmail.com/T/ [v1] Signed-off-by: Deepanshu Kartikey Reviewed-by: Krzysztof Kozlowski Signed-off-by: NipaLocal --- net/nfc/core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/nfc/core.c b/net/nfc/core.c index ae1c842f9c642..82f023f377541 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -1154,6 +1154,7 @@ EXPORT_SYMBOL(nfc_register_device); void nfc_unregister_device(struct nfc_dev *dev) { int rc; + struct rfkill *rfk = NULL; pr_debug("dev_name=%s\n", dev_name(&dev->dev)); @@ -1164,13 +1165,17 @@ void nfc_unregister_device(struct nfc_dev *dev) device_lock(&dev->dev); if (dev->rfkill) { - rfkill_unregister(dev->rfkill); - rfkill_destroy(dev->rfkill); + rfk = dev->rfkill; dev->rfkill = NULL; } dev->shutting_down = true; device_unlock(&dev->dev); + if (rfk) { + rfkill_unregister(rfk); + rfkill_destroy(rfk); + } + if (dev->ops->check_presence) { timer_delete_sync(&dev->check_pres_timer); cancel_work_sync(&dev->check_pres_work); From 78b2c3e3861f1b82f4579ea9334c48556125970b Mon Sep 17 00:00:00 2001 From: "Yury Norov (NVIDIA)" Date: Wed, 17 Dec 2025 20:57:57 -0500 Subject: [PATCH 146/214] i40e: drop useless bitmap_weight() call in i40e_set_rxfh_fields() bitmap_weight() is O(N) and useless here, because the following for_each_set_bit() returns immediately in case of empty flow_pctypes. Signed-off-by: Yury Norov (NVIDIA) Signed-off-by: NipaLocal --- .../net/ethernet/intel/i40e/i40e_ethtool.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 6a47ea0927e96..9fca52f42ce43 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -3625,6 +3625,7 @@ static int i40e_set_rxfh_fields(struct net_device *netdev, ((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32); DECLARE_BITMAP(flow_pctypes, FLOW_PCTYPES_SIZE); u64 i_set, i_setc; + u8 flow_id; bitmap_zero(flow_pctypes, FLOW_PCTYPES_SIZE); @@ -3708,20 +3709,14 @@ static int i40e_set_rxfh_fields(struct net_device *netdev, return -EINVAL; } - if (bitmap_weight(flow_pctypes, FLOW_PCTYPES_SIZE)) { - u8 flow_id; + for_each_set_bit(flow_id, flow_pctypes, FLOW_PCTYPES_SIZE) { + i_setc = (u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, flow_id)) | + ((u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, flow_id)) << 32); + i_set = i40e_get_rss_hash_bits(&pf->hw, nfc, i_setc); - for_each_set_bit(flow_id, flow_pctypes, FLOW_PCTYPES_SIZE) { - i_setc = (u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, flow_id)) | - ((u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, flow_id)) << 32); - i_set = i40e_get_rss_hash_bits(&pf->hw, nfc, i_setc); - - i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, flow_id), - (u32)i_set); - i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, flow_id), - (u32)(i_set >> 32)); - hena |= BIT_ULL(flow_id); - } + i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, flow_id), (u32)i_set); + i40e_write_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, flow_id), (u32)(i_set >> 32)); + hena |= BIT_ULL(flow_id); } i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena); From 5de770a58158ae5459f4d9d088640a0c48285f20 Mon Sep 17 00:00:00 2001 From: Qianchang Zhao Date: Thu, 18 Dec 2025 11:59:22 +0900 Subject: [PATCH 147/214] nfc: llcp: avoid double release/put on LLCP_CLOSED in nfc_llcp_recv_disc() nfc_llcp_sock_get() takes a reference on the LLCP socket via sock_hold(). In nfc_llcp_recv_disc(), when the socket is already in LLCP_CLOSED state, the code used to perform release_sock() and nfc_llcp_sock_put() in the CLOSED branch but then continued execution and later performed the same cleanup again on the common exit path. This results in refcount imbalance (double put) and unbalanced lock release. Remove the redundant CLOSED-branch cleanup so that release_sock() and nfc_llcp_sock_put() are performed exactly once via the common exit path, while keeping the existing DM_DISC reply behavior. Fixes: d646960f7986 ("NFC: Initial LLCP support") Cc: stable@vger.kernel.org Signed-off-by: Qianchang Zhao Signed-off-by: NipaLocal --- net/nfc/llcp_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index beeb3b4d28cab..ed37604ed2c82 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1177,11 +1177,6 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local, nfc_llcp_socket_purge(llcp_sock); - if (sk->sk_state == LLCP_CLOSED) { - release_sock(sk); - nfc_llcp_sock_put(llcp_sock); - } - if (sk->sk_state == LLCP_CONNECTED) { nfc_put_device(local->dev); sk->sk_state = LLCP_CLOSED; From aa3ca91ac0b572ddd916bbac68e537a5b7293c36 Mon Sep 17 00:00:00 2001 From: Qianchang Zhao Date: Thu, 18 Dec 2025 11:59:23 +0900 Subject: [PATCH 148/214] nfc: llcp: stop processing on LLCP_CLOSED in nfc_llcp_recv_hdlc() nfc_llcp_sock_get() takes a reference on the LLCP socket via sock_hold(). In nfc_llcp_recv_hdlc(), the LLCP_CLOSED branch releases the socket lock and drops the reference, but the function continues to operate on llcp_sock/sk and later runs release_sock() and nfc_llcp_sock_put() again on the common exit path. Return immediately after the CLOSED cleanup to avoid refcount/lock imbalance and to avoid using the socket after dropping the reference. Fixes: d646960f7986 ("NFC: Initial LLCP support") Cc: stable@vger.kernel.org Signed-off-by: Qianchang Zhao Signed-off-by: NipaLocal --- net/nfc/llcp_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index ed37604ed2c82..f6c1d79f91e4e 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1089,6 +1089,7 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, if (sk->sk_state == LLCP_CLOSED) { release_sock(sk); nfc_llcp_sock_put(llcp_sock); + return; } /* Pass the payload upstream */ From a1805472499eb4291d3914c661db72db966fd3c3 Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Thu, 18 Dec 2025 11:29:37 +0700 Subject: [PATCH 149/214] net: bridge: Describe @tunnel_hash member in net_bridge_vlan_group struct Sphinx reports kernel-doc warning: WARNING: ./net/bridge/br_private.h:267 struct member 'tunnel_hash' not described in 'net_bridge_vlan_group' Fix it by describing @tunnel_hash member. Fixes: efa5356b0d9753 ("bridge: per vlan dst_metadata netlink support") Signed-off-by: Bagas Sanjaya Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: NipaLocal --- net/bridge/br_private.h | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 7280c4e9305f3..b9b2981c48414 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -247,6 +247,7 @@ struct net_bridge_vlan { * struct net_bridge_vlan_group * * @vlan_hash: VLAN entry rhashtable + * @tunnel_hash: Hash table to map from tunnel key ID (e.g. VXLAN VNI) to VLAN * @vlan_list: sorted VLAN entry list * @num_vlans: number of total VLAN entries * @pvid: PVID VLAN id From 6085b47a5994e5ce49e756283b03016fb75e800f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 18 Dec 2025 08:18:44 +0000 Subject: [PATCH 150/214] net: avoid prefetching NULL pointers Aditya Gupta reported PowerPC crashes bisected to the blamed commit. Apparently some platforms do not allow prefetch() on arbitrary pointers. prefetch(next); prefetch(&next->priority); // CRASH when next == NULL Only NULL seems to be supported, with specific handling in prefetch(). Add a conditional to avoid the two prefetches and the skb->next clearing for the last skb in the list. Fixes: b2e9821cff6c ("net: prefech skb->priority in __dev_xmit_skb()") Reported-by: Aditya Gupta Closes: https://lore.kernel.org/netdev/e9f4abee-b132-440f-a50e-bced0868b5a7@linux.ibm.com/T/#mddc372b64ec5a3b181acc9ee3909110c391cc18a Signed-off-by: Eric Dumazet Tested-by: Aditya Gupta Signed-off-by: NipaLocal --- net/core/dev.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 9094c0fb8c689..36dc5199037ed 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4241,9 +4241,11 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, int count = 0; llist_for_each_entry_safe(skb, next, ll_list, ll_node) { - prefetch(next); - prefetch(&next->priority); - skb_mark_not_on_list(skb); + if (next) { + prefetch(next); + prefetch(&next->priority); + skb_mark_not_on_list(skb); + } rc = dev_qdisc_enqueue(skb, q, &to_free, txq); count++; } From 3762ae2b88de88c9238acbd0c40d7fd33ba98059 Mon Sep 17 00:00:00 2001 From: Mohammad Heib Date: Thu, 18 Dec 2025 14:13:21 +0200 Subject: [PATCH 151/214] i40e: drop udp_tunnel_get_rx_info() call from i40e_open() The i40e driver calls udp_tunnel_get_rx_info() during i40e_open(). This is redundant because UDP tunnel RX offload state is preserved across device down/up cycles. The udp_tunnel core handles synchronization automatically when required. Furthermore, recent changes in the udp_tunnel infrastructure require querying RX info while holding the udp_tunnel lock. Calling it directly from the ndo_open path violates this requirement, triggering the following lockdep warning: Call Trace: ? __udp_tunnel_nic_assert_locked+0x39/0x40 [udp_tunnel] i40e_open+0x135/0x14f [i40e] __dev_open+0x121/0x2e0 __dev_change_flags+0x227/0x270 dev_change_flags+0x3d/0xb0 devinet_ioctl+0x56f/0x860 sock_do_ioctl+0x7b/0x130 __x64_sys_ioctl+0x91/0xd0 do_syscall_64+0x90/0x170 ... Remove the redundant and unsafe call to udp_tunnel_get_rx_info() from i40e_open() resolve the locking violation. Fixes: 06a5f7f167c5 ("i40e: Move all UDP port notifiers to single function") Signed-off-by: Mohammad Heib Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0b1cc0481027a..d3bc3207054f9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9030,7 +9030,6 @@ int i40e_open(struct net_device *netdev) TCP_FLAG_FIN | TCP_FLAG_CWR) >> 16); wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16); - udp_tunnel_get_rx_info(netdev); return 0; } From 93312339ad42a28e60a0c4269ab54c1f47e54d56 Mon Sep 17 00:00:00 2001 From: Mohammad Heib Date: Thu, 18 Dec 2025 14:13:22 +0200 Subject: [PATCH 152/214] ice: drop udp_tunnel_get_rx_info() call from ndo_open() The ice driver calls udp_tunnel_get_rx_info() during ice_open_internal(). This is redundant because UDP tunnel RX offload state is preserved across device down/up cycles. The udp_tunnel core handles synchronization automatically when required. Furthermore, recent changes in the udp_tunnel infrastructure require querying RX info while holding the udp_tunnel lock. Calling it directly from the ndo_open path violates this requirement, triggering the following lockdep warning: Call Trace: ice_open_internal+0x253/0x350 [ice] __udp_tunnel_nic_assert_locked+0x86/0xb0 [udp_tunnel] __dev_open+0x2f5/0x880 __dev_change_flags+0x44c/0x660 netif_change_flags+0x80/0x160 devinet_ioctl+0xd21/0x15f0 inet_ioctl+0x311/0x350 sock_ioctl+0x114/0x220 __x64_sys_ioctl+0x131/0x1a0 ... Remove the redundant and unsafe call to udp_tunnel_get_rx_info() from ice_open_internal() to resolve the locking violation Fixes: a4e82a81f573 ("ice: Add support for tunnel offloads") Signed-off-by: Mohammad Heib Reviewed-by: Aleksandr Loktionov Signed-off-by: NipaLocal --- drivers/net/ethernet/intel/ice/ice_main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 4bb68e7a00f5f..a91f96253db0a 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -9631,9 +9631,6 @@ int ice_open_internal(struct net_device *netdev) netdev_err(netdev, "Failed to open VSI 0x%04X on switch 0x%04X\n", vsi->vsi_num, vsi->vsw->sw_id); - /* Update existing tunnels information */ - udp_tunnel_get_rx_info(netdev); - return err; } From d4824c2b11dabc6bcb5a21fb8d04ab0da2bc7e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20B=2E=20Marli=C3=A8re?= Date: Thu, 18 Dec 2025 09:20:38 -0300 Subject: [PATCH 153/214] selftests: net: fib-onlink-tests: Set high metric for default IPv6 route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the test breaks if the SUT already has a default route configured for IPv6. Fix by adding "metric 9999" to the `ip -6 ro add` command, so that multiple default routes can coexist. Fixes: 4ed591c8ab44 ("net/ipv6: Allow onlink routes to have a device mismatch if it is the default route") Signed-off-by: Ricardo B. Marlière Signed-off-by: NipaLocal --- tools/testing/selftests/net/fib-onlink-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh index ec2d6ceb1f08d..acf6b0617373a 100755 --- a/tools/testing/selftests/net/fib-onlink-tests.sh +++ b/tools/testing/selftests/net/fib-onlink-tests.sh @@ -207,7 +207,7 @@ setup() ip -netns ${PEER_NS} addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]} nodad done - ip -6 ro add default via ${V6ADDRS[p3]/::[0-9]/::64} + ip -6 ro add default via ${V6ADDRS[p3]/::[0-9]/::64} metric 9999 ip -6 ro add table ${VRF_TABLE} default via ${V6ADDRS[p7]/::[0-9]/::64} set +e From 4dbe380269be44a15c13badee381218e9dc9c4d1 Mon Sep 17 00:00:00 2001 From: Dipayaan Roy Date: Thu, 18 Dec 2025 05:10:54 -0800 Subject: [PATCH 154/214] net: mana: Fix use-after-free in reset service rescan path When mana_serv_reset() encounters -ETIMEDOUT or -EPROTO from mana_gd_resume(), it performs a PCI rescan via mana_serv_rescan(). mana_serv_rescan() calls pci_stop_and_remove_bus_device(), which can invoke the driver's remove path and free the gdma_context associated with the device. After returning, mana_serv_reset() currently jumps to the out label and attempts to clear gc->in_service, dereferencing a freed gdma_context. The issue was observed with the following call logs: [ 698.942636] BUG: unable to handle page fault for address: ff6c2b638088508d [ 698.943121] #PF: supervisor write access in kernel mode [ 698.943423] #PF: error_code(0x0002) - not-present page [S[ 698.943793] Pat Dec 6 07:GD5 100000067 P4D 1002f7067 PUD 1002f8067 PMD 101bef067 PTE 0 0:56 2025] hv_[n e 698.944283] Oops: Oops: 0002 [#1] SMP NOPTI tvsc f8615163-00[ 698.944611] CPU: 28 UID: 0 PID: 249 Comm: kworker/28:1 ... [Sat Dec 6 07:50:56 2025] R10: [ 699.121594] mana 7870:00:00.0 enP30832s1: Configured vPort 0 PD 18 DB 16 000000000000001b R11: 0000000000000000 R12: ff44cf3f40270000 [Sat Dec 6 07:50:56 2025] R13: 0000000000000001 R14: ff44cf3f402700c8 R15: ff44cf3f4021b405 [Sat Dec 6 07:50:56 2025] FS: 0000000000000000(0000) GS:ff44cf7e9fcf9000(0000) knlGS:0000000000000000 [Sat Dec 6 07:50:56 2025] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [Sat Dec 6 07:50:56 2025] CR2: ff6c2b638088508d CR3: 000000011fe43001 CR4: 0000000000b73ef0 [Sat Dec 6 07:50:56 2025] Call Trace: [Sat Dec 6 07:50:56 2025] [Sat Dec 6 07:50:56 2025] mana_serv_func+0x24/0x50 [mana] [Sat Dec 6 07:50:56 2025] process_one_work+0x190/0x350 [Sat Dec 6 07:50:56 2025] worker_thread+0x2b7/0x3d0 [Sat Dec 6 07:50:56 2025] kthread+0xf3/0x200 [Sat Dec 6 07:50:56 2025] ? __pfx_worker_thread+0x10/0x10 [Sat Dec 6 07:50:56 2025] ? __pfx_kthread+0x10/0x10 [Sat Dec 6 07:50:56 2025] ret_from_fork+0x21a/0x250 [Sat Dec 6 07:50:56 2025] ? __pfx_kthread+0x10/0x10 [Sat Dec 6 07:50:56 2025] ret_from_fork_asm+0x1a/0x30 [Sat Dec 6 07:50:56 2025] Fix this by returning immediately after mana_serv_rescan() to avoid accessing GC state that may no longer be valid. Fixes: 9bf66036d686 ("net: mana: Handle hardware recovery events when probing the device") Reviewed-by: Simon Horman Reviewed-by: Long Li Signed-off-by: Dipayaan Roy Signed-off-by: NipaLocal --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index efb4e412ec7e4..0055c231acf6d 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -481,7 +481,7 @@ static void mana_serv_reset(struct pci_dev *pdev) /* Perform PCI rescan on device if we failed on HWC */ dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n"); mana_serv_rescan(pdev); - goto out; + return; } if (ret) From 551950dae8e41e4308a62849a3042fb3d4b95ec7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 18 Dec 2025 07:59:13 -0700 Subject: [PATCH 155/214] af_unix: don't post cmsg for SO_INQ unless explicitly asked for A previous commit added SO_INQ support for AF_UNIX (SOCK_STREAM), but it posts a SCM_INQ cmsg even if just msg->msg_get_inq is set. This is incorrect, as ->msg_get_inq is just the caller asking for the remainder to be passed back in msg->msg_inq, it has nothing to do with cmsg. The original commit states that this is done to make sockets io_uring-friendly", but it's actually incorrect as io_uring doesn't use cmsg headers internally at all, and it's actively wrong as this means that cmsg's are always posted if someone does recvmsg via io_uring. Fix that up by only posting cmsg if u->recvmsg_inq is set. Cc: stable@vger.kernel.org Fixes: df30285b3670 ("af_unix: Introduce SO_INQ.") Reported-by: Julian Orth Link: https://github.com/axboe/liburing/issues/1509 Signed-off-by: Jens Axboe Signed-off-by: NipaLocal --- net/unix/af_unix.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 55cdebfa0da02..110d716087b54 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -3086,12 +3086,16 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, mutex_unlock(&u->iolock); if (msg) { + bool do_cmsg; + scm_recv_unix(sock, msg, &scm, flags); - if (READ_ONCE(u->recvmsg_inq) || msg->msg_get_inq) { + do_cmsg = READ_ONCE(u->recvmsg_inq); + if (do_cmsg || msg->msg_get_inq) { msg->msg_inq = READ_ONCE(u->inq_len); - put_cmsg(msg, SOL_SOCKET, SCM_INQ, - sizeof(msg->msg_inq), &msg->msg_inq); + if (do_cmsg) + put_cmsg(msg, SOL_SOCKET, SCM_INQ, + sizeof(msg->msg_inq), &msg->msg_inq); } } else { scm_destroy(&scm); From 65dab8cf5a7e085221b0f897a1d4242b646a5dcf Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 18 Dec 2025 07:59:14 -0700 Subject: [PATCH 156/214] af_unix: only post SO_INQ cmsg for non-error case As is done for TCP sockets, don't post an SCM_INQ cmsg for an error case. Only post them for the non-error case, which is when unix_stream_read_generic will return >= 0. Cc: stable@vger.kernel.org Fixes: df30285b3670 ("af_unix: Introduce SO_INQ.") Reported-by: Julian Orth Link: https://github.com/axboe/liburing/issues/1509 Signed-off-by: Jens Axboe Signed-off-by: NipaLocal --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 110d716087b54..72dc5d5bcac89 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -3091,7 +3091,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, scm_recv_unix(sock, msg, &scm, flags); do_cmsg = READ_ONCE(u->recvmsg_inq); - if (do_cmsg || msg->msg_get_inq) { + if ((do_cmsg || msg->msg_get_inq) && (copied ?: err) >= 0) { msg->msg_inq = READ_ONCE(u->inq_len); if (do_cmsg) put_cmsg(msg, SOL_SOCKET, SCM_INQ, From e7d58895af27fc8eb4b2fa4934cd988c263064dd Mon Sep 17 00:00:00 2001 From: Anshumali Gaur Date: Fri, 19 Dec 2025 11:52:26 +0530 Subject: [PATCH 157/214] octeontx2-pf: fix "UBSAN: shift-out-of-bounds error" This patch ensures that the RX ring size (rx_pending) is not set below the permitted length. This avoids UBSAN shift-out-of-bounds errors when users passes small or zero ring sizes via ethtool -G. Fixes: d45d8979840d ("octeontx2-pf: Add basic ethtool support") Signed-off-by: Anshumali Gaur Change-Id: I6de6770dbc0dd952725ccd71ce521f801bc7b15b Signed-off-by: NipaLocal --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index b90e23dc49de9..b6449f0a9e7dd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -418,6 +418,14 @@ static int otx2_set_ringparam(struct net_device *netdev, */ if (rx_count < pfvf->hw.rq_skid) rx_count = pfvf->hw.rq_skid; + + if (ring->rx_pending < 16) { + netdev_err(netdev, + "rx ring size %u invalid, min is 16\n", + ring->rx_pending); + return -EINVAL; + } + rx_count = Q_COUNT(Q_SIZE(rx_count, 3)); /* Due pipelining impact minimum 2000 unused SQ CQE's From 6371abc648ad55cbbfbdac43d8f1c7919421601e Mon Sep 17 00:00:00 2001 From: Vimlesh Kumar Date: Fri, 19 Dec 2025 07:29:52 +0000 Subject: [PATCH 158/214] octeon_ep: avoid compiler and IQ/OQ reordering Utilize READ_ONCE and WRITE_ONCE APIs for IO queue Tx/Rx variable access to prevent compiler optimization and reordering. Additionally, ensure IO queue OUT/IN_CNT registers are flushed by performing a read-back after writing. Relocate IQ/OQ IN/OUT_CNTS updates to occur before NAPI completion, and replace napi_complete with napi_complete_done. Fixes: 37d79d0596062 ("octeon_ep: add Tx/Rx processing and interrupt support") Signed-off-by: Sathesh Edara Signed-off-by: Shinas Rasheed Signed-off-by: Vimlesh Kumar Signed-off-by: NipaLocal --- .../ethernet/marvell/octeon_ep/octep_main.c | 40 +++++++++++++------ .../net/ethernet/marvell/octeon_ep/octep_rx.c | 27 +++++++++---- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index bcea3fc26a8c7..ccfb248d09148 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -555,28 +555,43 @@ static void octep_clean_irqs(struct octep_device *oct) } /** - * octep_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue. + * octep_update_pkt() - Update IQ/OQ IN/OUT_CNT registers. * * @iq: Octeon Tx queue data structure. * @oq: Octeon Rx queue data structure. */ -static void octep_enable_ioq_irq(struct octep_iq *iq, struct octep_oq *oq) +static void octep_update_pkt(struct octep_iq *iq, struct octep_oq *oq) { - u32 pkts_pend = oq->pkts_pending; + u32 pkts_pend = READ_ONCE(oq->pkts_pending); + u32 last_pkt_count = READ_ONCE(oq->last_pkt_count); + u32 pkts_processed = READ_ONCE(iq->pkts_processed); + u32 pkt_in_done = READ_ONCE(iq->pkt_in_done); netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no); - if (iq->pkts_processed) { - writel(iq->pkts_processed, iq->inst_cnt_reg); - iq->pkt_in_done -= iq->pkts_processed; - iq->pkts_processed = 0; + if (pkts_processed) { + writel(pkts_processed, iq->inst_cnt_reg); + readl(iq->inst_cnt_reg); + WRITE_ONCE(iq->pkt_in_done, (pkt_in_done - pkts_processed)); + WRITE_ONCE(iq->pkts_processed, 0); } - if (oq->last_pkt_count - pkts_pend) { - writel(oq->last_pkt_count - pkts_pend, oq->pkts_sent_reg); - oq->last_pkt_count = pkts_pend; + if (last_pkt_count - pkts_pend) { + writel(last_pkt_count - pkts_pend, oq->pkts_sent_reg); + readl(oq->pkts_sent_reg); + WRITE_ONCE(oq->last_pkt_count, pkts_pend); } /* Flush the previous wrties before writing to RESEND bit */ - wmb(); + smp_wmb(); +} + +/** + * octep_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue. + * + * @iq: Octeon Tx queue data structure. + * @oq: Octeon Rx queue data structure. + */ +static void octep_enable_ioq_irq(struct octep_iq *iq, struct octep_oq *oq) +{ writeq(1UL << OCTEP_OQ_INTR_RESEND_BIT, oq->pkts_sent_reg); writeq(1UL << OCTEP_IQ_INTR_RESEND_BIT, iq->inst_cnt_reg); } @@ -602,7 +617,8 @@ static int octep_napi_poll(struct napi_struct *napi, int budget) if (tx_pending || rx_done >= budget) return budget; - napi_complete(napi); + octep_update_pkt(ioq_vector->iq, ioq_vector->oq); + napi_complete_done(napi, rx_done); octep_enable_ioq_irq(ioq_vector->iq, ioq_vector->oq); return rx_done; } diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c index 82b6b19e76b47..11f1b45d0f920 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c @@ -318,10 +318,16 @@ static int octep_oq_check_hw_for_pkts(struct octep_device *oct, struct octep_oq *oq) { u32 pkt_count, new_pkts; + u32 last_pkt_count, pkts_pending; pkt_count = readl(oq->pkts_sent_reg); - new_pkts = pkt_count - oq->last_pkt_count; + last_pkt_count = READ_ONCE(oq->last_pkt_count); + new_pkts = pkt_count - last_pkt_count; + if (pkt_count < last_pkt_count) { + dev_err(oq->dev, "OQ-%u pkt_count(%u) < oq->last_pkt_count(%u)\n", + oq->q_no, pkt_count, last_pkt_count); + } /* Clear the hardware packets counter register if the rx queue is * being processed continuously with-in a single interrupt and * reached half its max value. @@ -332,8 +338,9 @@ static int octep_oq_check_hw_for_pkts(struct octep_device *oct, pkt_count = readl(oq->pkts_sent_reg); new_pkts += pkt_count; } - oq->last_pkt_count = pkt_count; - oq->pkts_pending += new_pkts; + WRITE_ONCE(oq->last_pkt_count, pkt_count); + pkts_pending = READ_ONCE(oq->pkts_pending); + WRITE_ONCE(oq->pkts_pending, (pkts_pending + new_pkts)); return new_pkts; } @@ -408,7 +415,7 @@ static int __octep_oq_process_rx(struct octep_device *oct, u16 rx_ol_flags; u32 read_idx; - read_idx = oq->host_read_idx; + read_idx = READ_ONCE(oq->host_read_idx); rx_bytes = 0; desc_used = 0; for (pkt = 0; pkt < pkts_to_process; pkt++) { @@ -493,7 +500,7 @@ static int __octep_oq_process_rx(struct octep_device *oct, napi_gro_receive(oq->napi, skb); } - oq->host_read_idx = read_idx; + WRITE_ONCE(oq->host_read_idx, read_idx); oq->refill_count += desc_used; oq->stats->packets += pkt; oq->stats->bytes += rx_bytes; @@ -516,22 +523,26 @@ int octep_oq_process_rx(struct octep_oq *oq, int budget) { u32 pkts_available, pkts_processed, total_pkts_processed; struct octep_device *oct = oq->octep_dev; + u32 pkts_pending; pkts_available = 0; pkts_processed = 0; total_pkts_processed = 0; while (total_pkts_processed < budget) { /* update pending count only when current one exhausted */ - if (oq->pkts_pending == 0) + pkts_pending = READ_ONCE(oq->pkts_pending); + if (pkts_pending == 0) octep_oq_check_hw_for_pkts(oct, oq); + pkts_pending = READ_ONCE(oq->pkts_pending); pkts_available = min(budget - total_pkts_processed, - oq->pkts_pending); + pkts_pending); if (!pkts_available) break; pkts_processed = __octep_oq_process_rx(oct, oq, pkts_available); - oq->pkts_pending -= pkts_processed; + pkts_pending = READ_ONCE(oq->pkts_pending); + WRITE_ONCE(oq->pkts_pending, (pkts_pending - pkts_processed)); total_pkts_processed += pkts_processed; } From 18bb2a274895307929ed5db68c2dff254a3389ec Mon Sep 17 00:00:00 2001 From: Vimlesh Kumar Date: Fri, 19 Dec 2025 07:29:53 +0000 Subject: [PATCH 159/214] octeon_ep_vf: avoid compiler and IQ/OQ reordering Utilize READ_ONCE and WRITE_ONCE APIs for IO queue Tx/Rx variable access to prevent compiler optimization and reordering. Additionally, ensure IO queue OUT/IN_CNT registers are flushed by performing a read-back after writing. Relocate IQ/OQ IN/OUT_CNTS updates to occur before NAPI completion, and replace napi_complete with napi_complete_done. Fixes: 1cd3b407977c3 ("octeon_ep_vf: add Tx/Rx processing and interrupt support") Signed-off-by: Sathesh Edara Signed-off-by: Shinas Rasheed Signed-off-by: Vimlesh Kumar Signed-off-by: NipaLocal --- .../marvell/octeon_ep_vf/octep_vf_main.c | 38 ++++++++++++++----- .../marvell/octeon_ep_vf/octep_vf_rx.c | 28 ++++++++++---- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c index 420c3f4cf7417..27a5fc38bccb6 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.c @@ -286,28 +286,45 @@ static void octep_vf_clean_irqs(struct octep_vf_device *oct) } /** - * octep_vf_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue. + * octep_vf_update_pkt() - Update IQ/OQ IN/OUT_CNT registers. * * @iq: Octeon Tx queue data structure. * @oq: Octeon Rx queue data structure. */ -static void octep_vf_enable_ioq_irq(struct octep_vf_iq *iq, struct octep_vf_oq *oq) + +static void octep_vf_update_pkt(struct octep_vf_iq *iq, struct octep_vf_oq *oq) { - u32 pkts_pend = oq->pkts_pending; + u32 pkts_pend = READ_ONCE(oq->pkts_pending); + u32 last_pkt_count = READ_ONCE(oq->last_pkt_count); + u32 pkts_processed = READ_ONCE(iq->pkts_processed); + u32 pkt_in_done = READ_ONCE(iq->pkt_in_done); netdev_dbg(iq->netdev, "enabling intr for Q-%u\n", iq->q_no); - if (iq->pkts_processed) { - writel(iq->pkts_processed, iq->inst_cnt_reg); - iq->pkt_in_done -= iq->pkts_processed; - iq->pkts_processed = 0; + if (pkts_processed) { + writel(pkts_processed, iq->inst_cnt_reg); + readl(iq->inst_cnt_reg); + WRITE_ONCE(iq->pkt_in_done, (pkt_in_done - pkts_processed)); + WRITE_ONCE(iq->pkts_processed, 0); } - if (oq->last_pkt_count - pkts_pend) { - writel(oq->last_pkt_count - pkts_pend, oq->pkts_sent_reg); - oq->last_pkt_count = pkts_pend; + if (last_pkt_count - pkts_pend) { + writel(last_pkt_count - pkts_pend, oq->pkts_sent_reg); + readl(oq->pkts_sent_reg); + WRITE_ONCE(oq->last_pkt_count, pkts_pend); } /* Flush the previous wrties before writing to RESEND bit */ smp_wmb(); +} + +/** + * octep_vf_enable_ioq_irq() - Enable MSI-x interrupt of a Tx/Rx queue. + * + * @iq: Octeon Tx queue data structure. + * @oq: Octeon Rx queue data structure. + */ +static void octep_vf_enable_ioq_irq(struct octep_vf_iq *iq, + struct octep_vf_oq *oq) +{ writeq(1UL << OCTEP_VF_OQ_INTR_RESEND_BIT, oq->pkts_sent_reg); writeq(1UL << OCTEP_VF_IQ_INTR_RESEND_BIT, iq->inst_cnt_reg); } @@ -333,6 +350,7 @@ static int octep_vf_napi_poll(struct napi_struct *napi, int budget) if (tx_pending || rx_done >= budget) return budget; + octep_vf_update_pkt(ioq_vector->iq, ioq_vector->oq); if (likely(napi_complete_done(napi, rx_done))) octep_vf_enable_ioq_irq(ioq_vector->iq, ioq_vector->oq); diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c index d70c8be3cfc40..31380962c2123 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c @@ -319,9 +319,16 @@ static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct, struct octep_vf_oq *oq) { u32 pkt_count, new_pkts; + u32 last_pkt_count, pkts_pending; pkt_count = readl(oq->pkts_sent_reg); - new_pkts = pkt_count - oq->last_pkt_count; + last_pkt_count = READ_ONCE(oq->last_pkt_count); + new_pkts = pkt_count - last_pkt_count; + + if (pkt_count < last_pkt_count) { + dev_err(oq->dev, "OQ-%u pkt_count(%u) < oq->last_pkt_count(%u)\n", + oq->q_no, pkt_count, last_pkt_count); + } /* Clear the hardware packets counter register if the rx queue is * being processed continuously with-in a single interrupt and @@ -333,8 +340,9 @@ static int octep_vf_oq_check_hw_for_pkts(struct octep_vf_device *oct, pkt_count = readl(oq->pkts_sent_reg); new_pkts += pkt_count; } - oq->last_pkt_count = pkt_count; - oq->pkts_pending += new_pkts; + WRITE_ONCE(oq->last_pkt_count, pkt_count); + pkts_pending = READ_ONCE(oq->pkts_pending); + WRITE_ONCE(oq->pkts_pending, (pkts_pending + new_pkts)); return new_pkts; } @@ -363,7 +371,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct, struct sk_buff *skb; u32 read_idx; - read_idx = oq->host_read_idx; + read_idx = READ_ONCE(oq->host_read_idx); rx_bytes = 0; desc_used = 0; for (pkt = 0; pkt < pkts_to_process; pkt++) { @@ -457,7 +465,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct, napi_gro_receive(oq->napi, skb); } - oq->host_read_idx = read_idx; + WRITE_ONCE(oq->host_read_idx, read_idx); oq->refill_count += desc_used; oq->stats->packets += pkt; oq->stats->bytes += rx_bytes; @@ -480,22 +488,26 @@ int octep_vf_oq_process_rx(struct octep_vf_oq *oq, int budget) { u32 pkts_available, pkts_processed, total_pkts_processed; struct octep_vf_device *oct = oq->octep_vf_dev; + u32 pkts_pending; pkts_available = 0; pkts_processed = 0; total_pkts_processed = 0; while (total_pkts_processed < budget) { /* update pending count only when current one exhausted */ - if (oq->pkts_pending == 0) + pkts_pending = READ_ONCE(oq->pkts_pending); + if (pkts_pending == 0) octep_vf_oq_check_hw_for_pkts(oct, oq); + pkts_pending = READ_ONCE(oq->pkts_pending); pkts_available = min(budget - total_pkts_processed, - oq->pkts_pending); + pkts_pending); if (!pkts_available) break; pkts_processed = __octep_vf_oq_process_rx(oct, oq, pkts_available); - oq->pkts_pending -= pkts_processed; + pkts_pending = READ_ONCE(oq->pkts_pending); + WRITE_ONCE(oq->pkts_pending, (pkts_pending - pkts_processed)); total_pkts_processed += pkts_processed; } From 5c51361200f0bbab51ae26b9f19657150820f452 Mon Sep 17 00:00:00 2001 From: Vimlesh Kumar Date: Fri, 19 Dec 2025 10:07:47 +0000 Subject: [PATCH 160/214] octeon_ep: disable per ring interrupts Disable the MSI-X per ring interrupt for every PF ring when PF netdev goes down. Fixes: 1f2c2d0cee023 ("octeon_ep: add hardware configuration APIs") Signed-off-by: Sathesh Edara Signed-off-by: Shinas Rasheed Signed-off-by: Vimlesh Kumar Signed-off-by: NipaLocal --- .../ethernet/marvell/octeon_ep/octep_cn9k_pf.c | 18 +++++++++++++++--- .../ethernet/marvell/octeon_ep/octep_cnxk_pf.c | 18 +++++++++++++++--- .../marvell/octeon_ep/octep_regs_cn9k_pf.h | 1 + .../marvell/octeon_ep/octep_regs_cnxk_pf.h | 1 + 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c index b5805969404fa..2574a6061e3d6 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c @@ -696,14 +696,26 @@ static void octep_enable_interrupts_cn93_pf(struct octep_device *oct) /* Disable all interrupts */ static void octep_disable_interrupts_cn93_pf(struct octep_device *oct) { - u64 intr_mask = 0ULL; + u64 reg_val, intr_mask = 0ULL; int srn, num_rings, i; srn = CFG_GET_PORTS_PF_SRN(oct->conf); num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); - for (i = 0; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); + for (i = 0; i < num_rings; i++) { + intr_mask |= (BIT_ULL(srn + i)); + reg_val = octep_read_csr64(oct, + CN93_SDP_R_IN_INT_LEVELS(srn + i)); + reg_val &= (~CN93_INT_ENA_BIT); + octep_write_csr64(oct, + CN93_SDP_R_IN_INT_LEVELS(srn + i), reg_val); + + reg_val = octep_read_csr64(oct, + CN93_SDP_R_OUT_INT_LEVELS(srn + i)); + reg_val &= (~CN93_INT_ENA_BIT); + octep_write_csr64(oct, + CN93_SDP_R_OUT_INT_LEVELS(srn + i), reg_val); + } octep_write_csr64(oct, CN93_SDP_EPF_IRERR_RINT_ENA_W1C, intr_mask); octep_write_csr64(oct, CN93_SDP_EPF_ORERR_RINT_ENA_W1C, intr_mask); diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c index 5de0b5ecbc5fd..73cd0ca758f08 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c @@ -720,14 +720,26 @@ static void octep_enable_interrupts_cnxk_pf(struct octep_device *oct) /* Disable all interrupts */ static void octep_disable_interrupts_cnxk_pf(struct octep_device *oct) { - u64 intr_mask = 0ULL; + u64 reg_val, intr_mask = 0ULL; int srn, num_rings, i; srn = CFG_GET_PORTS_PF_SRN(oct->conf); num_rings = CFG_GET_PORTS_ACTIVE_IO_RINGS(oct->conf); - for (i = 0; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); + for (i = 0; i < num_rings; i++) { + intr_mask |= BIT_ULL(srn + i); + reg_val = octep_read_csr64(oct, + CNXK_SDP_R_IN_INT_LEVELS(srn + i)); + reg_val &= (~CNXK_INT_ENA_BIT); + octep_write_csr64(oct, + CNXK_SDP_R_IN_INT_LEVELS(srn + i), reg_val); + + reg_val = octep_read_csr64(oct, + CNXK_SDP_R_OUT_INT_LEVELS(srn + i)); + reg_val &= (~CNXK_INT_ENA_BIT); + octep_write_csr64(oct, + CNXK_SDP_R_OUT_INT_LEVELS(srn + i), reg_val); + } octep_write_csr64(oct, CNXK_SDP_EPF_IRERR_RINT_ENA_W1C, intr_mask); octep_write_csr64(oct, CNXK_SDP_EPF_ORERR_RINT_ENA_W1C, intr_mask); diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h b/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h index ca473502d7a02..42cb199bd0855 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h @@ -386,5 +386,6 @@ #define CN93_PEM_BAR4_INDEX 7 #define CN93_PEM_BAR4_INDEX_SIZE 0x400000ULL #define CN93_PEM_BAR4_INDEX_OFFSET (CN93_PEM_BAR4_INDEX * CN93_PEM_BAR4_INDEX_SIZE) +#define CN93_INT_ENA_BIT (BIT_ULL(62)) #endif /* _OCTEP_REGS_CN9K_PF_H_ */ diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h b/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h index e637d7c8224d4..9eaadded9c503 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h @@ -412,5 +412,6 @@ #define CNXK_PEM_BAR4_INDEX 7 #define CNXK_PEM_BAR4_INDEX_SIZE 0x400000ULL #define CNXK_PEM_BAR4_INDEX_OFFSET (CNXK_PEM_BAR4_INDEX * CNXK_PEM_BAR4_INDEX_SIZE) +#define CNXK_INT_ENA_BIT (BIT_ULL(62)) #endif /* _OCTEP_REGS_CNXK_PF_H_ */ From 8263c85320990f20c69881b0f0caafeaca00c0da Mon Sep 17 00:00:00 2001 From: Vimlesh Kumar Date: Fri, 19 Dec 2025 10:07:48 +0000 Subject: [PATCH 161/214] octeon_ep: ensure dbell BADDR updation Make sure the OUT DBELL base address reflects the latest values written to it. Fix: Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures. Fixes: 0807dc76f3bf5 ("octeon_ep: support Octeon CN10K devices") Signed-off-by: Sathesh Edara Signed-off-by: Shinas Rasheed Signed-off-by: Vimlesh Kumar Signed-off-by: NipaLocal --- .../marvell/octeon_ep/octep_cn9k_pf.c | 3 +- .../marvell/octeon_ep/octep_cnxk_pf.c | 37 ++++++++++++++++--- .../ethernet/marvell/octeon_ep/octep_main.h | 2 +- .../net/ethernet/marvell/octeon_ep/octep_rx.c | 4 +- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c index 2574a6061e3d6..2a5cebbf1ff81 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c @@ -307,7 +307,7 @@ static void octep_setup_iq_regs_cn93_pf(struct octep_device *oct, int iq_no) } /* Setup registers for a hardware Rx Queue */ -static void octep_setup_oq_regs_cn93_pf(struct octep_device *oct, int oq_no) +static int octep_setup_oq_regs_cn93_pf(struct octep_device *oct, int oq_no) { u64 reg_val; u64 oq_ctl = 0ULL; @@ -355,6 +355,7 @@ static void octep_setup_oq_regs_cn93_pf(struct octep_device *oct, int oq_no) reg_val = ((u64)time_threshold << 32) | CFG_GET_OQ_INTR_PKT(oct->conf); octep_write_csr64(oct, CN93_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); + return 0; } /* Setup registers for a PF mailbox */ diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c index 73cd0ca758f08..1ca8da3f7dc72 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "octep_config.h" #include "octep_main.h" @@ -327,11 +328,13 @@ static void octep_setup_iq_regs_cnxk_pf(struct octep_device *oct, int iq_no) } /* Setup registers for a hardware Rx Queue */ -static void octep_setup_oq_regs_cnxk_pf(struct octep_device *oct, int oq_no) +static int octep_setup_oq_regs_cnxk_pf(struct octep_device *oct, int oq_no) { u64 reg_val; u64 oq_ctl = 0ULL; + u64 reg_ba_val; u32 time_threshold = 0; + unsigned long t_out_jiffies; struct octep_oq *oq = oct->oq[oq_no]; oq_no += CFG_GET_PORTS_PF_SRN(oct->conf); @@ -343,6 +346,33 @@ static void octep_setup_oq_regs_cnxk_pf(struct octep_device *oct, int oq_no) reg_val = octep_read_csr64(oct, CNXK_SDP_R_OUT_CONTROL(oq_no)); } while (!(reg_val & CNXK_R_OUT_CTL_IDLE)); } + octep_write_csr64(oct, CNXK_SDP_R_OUT_WMARK(oq_no), oq->max_count); + /* Wait for WMARK to get applied */ + usleep_range(10, 15); + + octep_write_csr64(oct, CNXK_SDP_R_OUT_SLIST_BADDR(oq_no), + oq->desc_ring_dma); + octep_write_csr64(oct, CNXK_SDP_R_OUT_SLIST_RSIZE(oq_no), + oq->max_count); + reg_ba_val = octep_read_csr64(oct, CNXK_SDP_R_OUT_SLIST_BADDR(oq_no)); + + if (reg_ba_val != oq->desc_ring_dma) { + t_out_jiffies = jiffies + 10 * HZ; + do { + if (reg_ba_val == ULLONG_MAX) + return -EFAULT; + octep_write_csr64(oct, + CNXK_SDP_R_OUT_SLIST_BADDR(oq_no), + oq->desc_ring_dma); + octep_write_csr64(oct, + CNXK_SDP_R_OUT_SLIST_RSIZE(oq_no), + oq->max_count); + reg_ba_val = + octep_read_csr64(oct, + CNXK_SDP_R_OUT_SLIST_BADDR(oq_no)); + } while ((reg_ba_val != oq->desc_ring_dma) && + time_before(jiffies, t_out_jiffies)); + } reg_val &= ~(CNXK_R_OUT_CTL_IMODE); reg_val &= ~(CNXK_R_OUT_CTL_ROR_P); @@ -356,10 +386,6 @@ static void octep_setup_oq_regs_cnxk_pf(struct octep_device *oct, int oq_no) reg_val |= (CNXK_R_OUT_CTL_ES_P); octep_write_csr64(oct, CNXK_SDP_R_OUT_CONTROL(oq_no), reg_val); - octep_write_csr64(oct, CNXK_SDP_R_OUT_SLIST_BADDR(oq_no), - oq->desc_ring_dma); - octep_write_csr64(oct, CNXK_SDP_R_OUT_SLIST_RSIZE(oq_no), - oq->max_count); oq_ctl = octep_read_csr64(oct, CNXK_SDP_R_OUT_CONTROL(oq_no)); @@ -385,6 +411,7 @@ static void octep_setup_oq_regs_cnxk_pf(struct octep_device *oct, int oq_no) reg_val &= ~0xFFFFFFFFULL; reg_val |= CFG_GET_OQ_WMARK(oct->conf); octep_write_csr64(oct, CNXK_SDP_R_OUT_WMARK(oq_no), reg_val); + return 0; } /* Setup registers for a PF mailbox */ diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h index 81ac4267811c8..35d0ff289a70d 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h @@ -77,7 +77,7 @@ struct octep_pci_win_regs { struct octep_hw_ops { void (*setup_iq_regs)(struct octep_device *oct, int q); - void (*setup_oq_regs)(struct octep_device *oct, int q); + int (*setup_oq_regs)(struct octep_device *oct, int q); void (*setup_mbox_regs)(struct octep_device *oct, int mbox); irqreturn_t (*mbox_intr_handler)(void *ioq_vector); diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c index 11f1b45d0f920..6f94ccafb6519 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c @@ -170,7 +170,9 @@ static int octep_setup_oq(struct octep_device *oct, int q_no) goto oq_fill_buff_err; octep_oq_reset_indices(oq); - oct->hw_ops.setup_oq_regs(oct, q_no); + if (oct->hw_ops.setup_oq_regs(oct, q_no)) + goto oq_fill_buff_err; + oct->num_oqs++; return 0; From 062dfd93e2d620c1d28bf488cd7c988545d795ad Mon Sep 17 00:00:00 2001 From: Vimlesh Kumar Date: Fri, 19 Dec 2025 10:07:49 +0000 Subject: [PATCH 162/214] octeon_ep_vf: ensure dbell BADDR updation Make sure the OUT DBELL base address reflects the latest values written to it. Fix: Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures. Fixes: 2c0c32c72be29 ("octeon_ep_vf: add hardware configuration APIs") Signed-off-by: Sathesh Edara Signed-off-by: Shinas Rasheed Signed-off-by: Vimlesh Kumar Signed-off-by: NipaLocal --- .../marvell/octeon_ep_vf/octep_vf_cn9k.c | 3 +- .../marvell/octeon_ep_vf/octep_vf_cnxk.c | 36 +++++++++++++++++-- .../marvell/octeon_ep_vf/octep_vf_main.h | 2 +- .../marvell/octeon_ep_vf/octep_vf_rx.c | 4 ++- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c index 88937fce75f14..4c769b27c2789 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c @@ -196,7 +196,7 @@ static void octep_vf_setup_iq_regs_cn93(struct octep_vf_device *oct, int iq_no) } /* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) +static int octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) { struct octep_vf_oq *oq = oct->oq[oq_no]; u32 time_threshold = 0; @@ -239,6 +239,7 @@ static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) time_threshold = CFG_GET_OQ_INTR_TIME(oct->conf); reg_val = ((u64)time_threshold << 32) | CFG_GET_OQ_INTR_PKT(oct->conf); octep_vf_write_csr64(oct, CN93_VF_SDP_R_OUT_INT_LEVELS(oq_no), reg_val); + return 0; } /* Setup registers for a VF mailbox */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c index 1f79dfad42c62..3967d0e0de829 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c @@ -199,12 +199,14 @@ static void octep_vf_setup_iq_regs_cnxk(struct octep_vf_device *oct, int iq_no) } /* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) +static int octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) { struct octep_vf_oq *oq = oct->oq[oq_no]; u32 time_threshold = 0; u64 oq_ctl = ULL(0); + u64 reg_ba_val; u64 reg_val; + unsigned long t_out_jiffies; reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); @@ -214,6 +216,35 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); } while (!(reg_val & CNXK_VF_R_OUT_CTL_IDLE)); } + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no), + oq->max_count); + /* Wait for WMARK to get applied */ + usleep_range(10, 15); + + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no), + oq->desc_ring_dma); + octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(oq_no), + oq->max_count); + reg_ba_val = octep_vf_read_csr64(oct, + CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no)); + if (reg_ba_val != oq->desc_ring_dma) { + t_out_jiffies = jiffies + 10 * HZ; + do { + if (reg_ba_val == ULLONG_MAX) + return -EFAULT; + octep_vf_write_csr64(oct, + CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no), oq->desc_ring_dma); + octep_vf_write_csr64(oct, + CNXK_VF_SDP_R_OUT_SLIST_RSIZE + (oq_no), oq->max_count); + reg_ba_val = + octep_vf_read_csr64(oct, + CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no)); + } while ((reg_ba_val != oq->desc_ring_dma) && + time_before(jiffies, t_out_jiffies)); + } reg_val &= ~(CNXK_VF_R_OUT_CTL_IMODE); reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_P); @@ -227,8 +258,6 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val |= (CNXK_VF_R_OUT_CTL_ES_P); octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no), reg_val); - octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_BADDR(oq_no), oq->desc_ring_dma); - octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_SLIST_RSIZE(oq_no), oq->max_count); oq_ctl = octep_vf_read_csr64(oct, CNXK_VF_SDP_R_OUT_CONTROL(oq_no)); /* Clear the ISIZE and BSIZE (22-0) */ @@ -250,6 +279,7 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val &= ~GENMASK_ULL(31, 0); reg_val |= CFG_GET_OQ_WMARK(oct->conf); octep_vf_write_csr64(oct, CNXK_VF_SDP_R_OUT_WMARK(oq_no), reg_val); + return 0; } /* Setup registers for a VF mailbox */ diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h index b9f13506f4620..c74cd2369e90d 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h @@ -55,7 +55,7 @@ struct octep_vf_mmio { struct octep_vf_hw_ops { void (*setup_iq_regs)(struct octep_vf_device *oct, int q); - void (*setup_oq_regs)(struct octep_vf_device *oct, int q); + int (*setup_oq_regs)(struct octep_vf_device *oct, int q); void (*setup_mbox_regs)(struct octep_vf_device *oct, int mbox); irqreturn_t (*non_ioq_intr_handler)(void *ioq_vector); diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c index 31380962c2123..08b11c232f698 100644 --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c @@ -171,7 +171,9 @@ static int octep_vf_setup_oq(struct octep_vf_device *oct, int q_no) goto oq_fill_buff_err; octep_vf_oq_reset_indices(oq); - oct->hw_ops.setup_oq_regs(oct, q_no); + if (oct->hw_ops.setup_oq_regs(oct, q_no)) + goto oq_fill_buff_err; + oct->num_oqs++; return 0; From 551998fecab53fa8b5572d6cd58a592d02e79797 Mon Sep 17 00:00:00 2001 From: Ankit Garg Date: Fri, 19 Dec 2025 10:29:45 +0000 Subject: [PATCH 163/214] gve: defer interrupt enabling until NAPI registration Currently, interrupts are automatically enabled immediately upon request. This allows interrupt to fire before the associated NAPI context is fully initialized and cause failures like below: [ 0.946369] Call Trace: [ 0.946369] [ 0.946369] __napi_poll+0x2a/0x1e0 [ 0.946369] net_rx_action+0x2f9/0x3f0 [ 0.946369] handle_softirqs+0xd6/0x2c0 [ 0.946369] ? handle_edge_irq+0xc1/0x1b0 [ 0.946369] __irq_exit_rcu+0xc3/0xe0 [ 0.946369] common_interrupt+0x81/0xa0 [ 0.946369] [ 0.946369] [ 0.946369] asm_common_interrupt+0x22/0x40 [ 0.946369] RIP: 0010:pv_native_safe_halt+0xb/0x10 Use the `IRQF_NO_AUTOEN` flag when requesting interrupts to prevent auto enablement and explicitly enable the interrupt in NAPI initialization path (and disable it during NAPI teardown). This ensures that interrupt lifecycle is strictly coupled with readiness of NAPI context. Cc: stable@vger.kernel.org Fixes: 1dfc2e46117e ("gve: Refactor napi add and remove functions") Signed-off-by: Ankit Garg Reviewed-by: Jordan Rhee Reviewed-by: Joshua Washington Signed-off-by: Harshitha Ramamurthy Signed-off-by: NipaLocal --- drivers/net/ethernet/google/gve/gve_main.c | 2 +- drivers/net/ethernet/google/gve/gve_utils.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index a7a088a77f378..7eb64e1e4d858 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -558,7 +558,7 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv) block->priv = priv; err = request_irq(priv->msix_vectors[msix_idx].vector, gve_is_gqi(priv) ? gve_intr : gve_intr_dqo, - 0, block->name, block); + IRQF_NO_AUTOEN, block->name, block); if (err) { dev_err(&priv->pdev->dev, "Failed to receive msix vector %d\n", i); diff --git a/drivers/net/ethernet/google/gve/gve_utils.c b/drivers/net/ethernet/google/gve/gve_utils.c index ace9b8698021f..b53b7fcdcdaf1 100644 --- a/drivers/net/ethernet/google/gve/gve_utils.c +++ b/drivers/net/ethernet/google/gve/gve_utils.c @@ -112,11 +112,13 @@ void gve_add_napi(struct gve_priv *priv, int ntfy_idx, netif_napi_add_locked(priv->dev, &block->napi, gve_poll); netif_napi_set_irq_locked(&block->napi, block->irq); + enable_irq(block->irq); } void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) { struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; + disable_irq(block->irq); netif_napi_del_locked(&block->napi); } From 2cae4a1d825e003bef33b67a96ba96d0358652c9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:16 +0100 Subject: [PATCH 164/214] dt-bindings: net: qcom: document the ethqos device for SCMI-based systems Describe the firmware-managed variant of the QCom DesignWare MAC. As the properties here differ a lot from the HLOS-managed variant, lets put it in a separate file. Since we need to update the maximum number of power domains, let's update existing bindings referencing the top-level snps,dwmac.yaml and limit their maxItems for power-domains to 1. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Reviewed-by: Martin Blumenstingl Signed-off-by: NipaLocal --- .../net/allwinner,sun7i-a20-gmac.yaml | 3 + .../bindings/net/altr,socfpga-stmmac.yaml | 3 + .../bindings/net/amlogic,meson-dwmac.yaml | 3 + .../bindings/net/eswin,eic7700-eth.yaml | 3 + .../bindings/net/intel,dwmac-plat.yaml | 3 + .../bindings/net/loongson,ls1b-gmac.yaml | 3 + .../bindings/net/loongson,ls1c-emac.yaml | 3 + .../bindings/net/nxp,dwmac-imx.yaml | 3 + .../bindings/net/nxp,lpc1850-dwmac.yaml | 3 + .../bindings/net/nxp,s32-dwmac.yaml | 3 + .../devicetree/bindings/net/qcom,ethqos.yaml | 3 + .../bindings/net/qcom,sa8255p-ethqos.yaml | 98 +++++++++++++++++++ .../bindings/net/renesas,rzn1-gmac.yaml | 3 + .../bindings/net/renesas,rzv2h-gbeth.yaml | 3 + .../bindings/net/rockchip-dwmac.yaml | 3 + .../devicetree/bindings/net/snps,dwmac.yaml | 5 +- .../bindings/net/sophgo,cv1800b-dwmac.yaml | 3 + .../bindings/net/sophgo,sg2044-dwmac.yaml | 3 + .../bindings/net/starfive,jh7110-dwmac.yaml | 3 + .../devicetree/bindings/net/stm32-dwmac.yaml | 3 + .../bindings/net/tesla,fsd-ethqos.yaml | 3 + .../bindings/net/thead,th1520-gmac.yaml | 3 + .../bindings/net/toshiba,visconti-dwmac.yaml | 3 + MAINTAINERS | 1 + 24 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/net/qcom,sa8255p-ethqos.yaml diff --git a/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml index 23e92be33ac86..b12632545673b 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml @@ -40,6 +40,9 @@ properties: description: PHY regulator + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml b/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml index fc445ad5a1f1a..448e617cddc4c 100644 --- a/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml +++ b/Documentation/devicetree/bindings/net/altr,socfpga-stmmac.yaml @@ -140,6 +140,9 @@ properties: - description: offset of the control register - description: shift within the control register + power-domains: + maxItems: 1 + patternProperties: "^mdio[0-9]$": type: object diff --git a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml index 5c91716d1f21e..9c9cc3ef384da 100644 --- a/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml @@ -158,6 +158,9 @@ properties: interrupt-names: const: macirq + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml index 91e8cd1db67b8..c1b67cfa76d07 100644 --- a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml +++ b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml @@ -83,6 +83,9 @@ properties: register - description: Offset of register controlling TX/RX clock delay + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/intel,dwmac-plat.yaml b/Documentation/devicetree/bindings/net/intel,dwmac-plat.yaml index 62c1da36a2b5a..e41851931b947 100644 --- a/Documentation/devicetree/bindings/net/intel,dwmac-plat.yaml +++ b/Documentation/devicetree/bindings/net/intel,dwmac-plat.yaml @@ -47,6 +47,9 @@ properties: interrupt-names: const: macirq + power-domains: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml b/Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml index c4f3224bad387..c9a131b8d8304 100644 --- a/Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml +++ b/Documentation/devicetree/bindings/net/loongson,ls1b-gmac.yaml @@ -66,6 +66,9 @@ properties: - mii - rgmii-id + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml b/Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml index 99001b940b836..49db18423dd80 100644 --- a/Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml +++ b/Documentation/devicetree/bindings/net/loongson,ls1c-emac.yaml @@ -65,6 +65,9 @@ properties: - mii - rmii + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml b/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml index e5db346beca96..b240c76e7dd52 100644 --- a/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml +++ b/Documentation/devicetree/bindings/net/nxp,dwmac-imx.yaml @@ -83,6 +83,9 @@ properties: description: To select RMII reference clock from external. + power-domains: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml b/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml index 05acd9bc76163..f61188ab0dbe3 100644 --- a/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/nxp,lpc1850-dwmac.yaml @@ -51,6 +51,9 @@ properties: items: - const: stmmaceth + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml b/Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml index 2b8b74c5feec8..716407a750796 100644 --- a/Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml @@ -52,6 +52,9 @@ properties: - const: rx - const: ptp_ref + power-domains: + maxItems: 1 + required: - clocks - clock-names diff --git a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml index 423959cb928d9..ef520f8105773 100644 --- a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml +++ b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml @@ -86,6 +86,9 @@ properties: phy-names: const: serdes + power-domains: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/net/qcom,sa8255p-ethqos.yaml b/Documentation/devicetree/bindings/net/qcom,sa8255p-ethqos.yaml new file mode 100644 index 0000000000000..72bb764c0ca04 --- /dev/null +++ b/Documentation/devicetree/bindings/net/qcom,sa8255p-ethqos.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/qcom,sa8255p-ethqos.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Ethernet ETHQOS device (firmware managed) + +maintainers: + - Bjorn Andersson + - Konrad Dybcio + - Bartosz Golaszewski + +description: + dwmmac based Qualcomm ethernet devices which support Gigabit + ethernet (version v2.3.0 and onwards) with clocks, interconnects, etc. + managed by firmware + +allOf: + - $ref: snps,dwmac.yaml# + +properties: + compatible: + const: qcom,sa8255p-ethqos + + reg: + maxItems: 2 + + reg-names: + items: + - const: stmmaceth + - const: rgmii + + interrupts: + items: + - description: Combined signal for various interrupt events + - description: The interrupt that occurs when HW safety error triggered + + interrupt-names: + items: + - const: macirq + - const: sfty + + power-domains: + minItems: 3 + maxItems: 3 + + power-domain-names: + items: + - const: core + - const: mdio + - const: serdes + + iommus: + maxItems: 1 + + dma-coherent: true + +required: + - compatible + - reg-names + - power-domains + - power-domain-names + +unevaluatedProperties: false + +examples: + - | + #include + + ethernet: ethernet@7a80000 { + compatible = "qcom,sa8255p-ethqos"; + reg = <0x23040000 0x10000>, + <0x23056000 0x100>; + reg-names = "stmmaceth", "rgmii"; + + iommus = <&apps_smmu 0x120 0x7>; + + interrupts = , + ; + interrupt-names = "macirq", "sfty"; + + dma-coherent; + + snps,tso; + snps,pbl = <32>; + rx-fifo-depth = <16384>; + tx-fifo-depth = <16384>; + + phy-handle = <ðernet_phy>; + phy-mode = "2500base-x"; + + snps,mtl-rx-config = <&mtl_rx_setup1>; + snps,mtl-tx-config = <&mtl_tx_setup1>; + + power-domains = <&scmi8_pd 0>, <&scmi8_pd 1>, <&scmi8_dvfs 0>; + power-domain-names = "core", "mdio", "serdes"; + }; diff --git a/Documentation/devicetree/bindings/net/renesas,rzn1-gmac.yaml b/Documentation/devicetree/bindings/net/renesas,rzn1-gmac.yaml index 16dd7a2631abf..ed0d10a19ca4c 100644 --- a/Documentation/devicetree/bindings/net/renesas,rzn1-gmac.yaml +++ b/Documentation/devicetree/bindings/net/renesas,rzn1-gmac.yaml @@ -44,6 +44,9 @@ properties: phandle pointing to a PCS sub-node compatible with renesas,rzn1-miic.yaml# + power-domains: + maxItems: 1 + required: - compatible diff --git a/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml b/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml index bd53ab300f500..bc5054b05f6d7 100644 --- a/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml +++ b/Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml @@ -123,6 +123,9 @@ properties: Documentation/devicetree/bindings/net/pcs/renesas,rzn1-miic.yaml# (Refer RZ/T2H portion in the DT-binding file) + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml index d17112527dab0..ef82ff2a2884d 100644 --- a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml @@ -121,6 +121,9 @@ properties: phy-supply: description: PHY regulator + power-domains: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index dd3c72e8363e7..312d1bbc2ad10 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -71,6 +71,7 @@ properties: - loongson,ls7a-dwmac - nxp,s32g2-dwmac - qcom,qcs404-ethqos + - qcom,sa8255p-ethqos - qcom,sa8775p-ethqos - qcom,sc8280xp-ethqos - qcom,sm8150-ethqos @@ -180,7 +181,8 @@ properties: - const: ahb power-domains: - maxItems: 1 + minItems: 1 + maxItems: 3 mac-mode: $ref: ethernet-controller.yaml#/properties/phy-connection-type @@ -643,6 +645,7 @@ allOf: - ingenic,x1830-mac - ingenic,x2000-mac - qcom,qcs404-ethqos + - qcom,sa8255p-ethqos - qcom,sa8775p-ethqos - qcom,sc8280xp-ethqos - qcom,sm8150-ethqos diff --git a/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml b/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml index b89456f0ef830..e78cbf594c695 100644 --- a/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/sophgo,cv1800b-dwmac.yaml @@ -49,6 +49,9 @@ properties: reset-names: const: stmmaceth + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml index e8d3814db0e94..845e2c67d2003 100644 --- a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml @@ -52,6 +52,9 @@ properties: interrupt-names: maxItems: 1 + power-domains: + maxItems: 1 + resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml index 313a153316612..8a68c6d7b5c6d 100644 --- a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml @@ -71,6 +71,9 @@ properties: The argument one is the offset of phy mode selection, the argument two is the shift of phy mode selection. + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/stm32-dwmac.yaml b/Documentation/devicetree/bindings/net/stm32-dwmac.yaml index 987254900d0da..29b878079ff0c 100644 --- a/Documentation/devicetree/bindings/net/stm32-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/stm32-dwmac.yaml @@ -121,6 +121,9 @@ properties: minItems: 1 maxItems: 2 + power-domains: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/net/tesla,fsd-ethqos.yaml b/Documentation/devicetree/bindings/net/tesla,fsd-ethqos.yaml index dd7481bb16e59..ad635529d676e 100644 --- a/Documentation/devicetree/bindings/net/tesla,fsd-ethqos.yaml +++ b/Documentation/devicetree/bindings/net/tesla,fsd-ethqos.yaml @@ -67,6 +67,9 @@ properties: - rgmii-rxid - rgmii-txid + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml b/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml index b3492a9aa4eff..c859f8bb5d582 100644 --- a/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml +++ b/Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml @@ -78,6 +78,9 @@ properties: items: - const: macirq + power-domains: + maxItems: 1 + required: - clocks - clock-names diff --git a/Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml b/Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml index f0f32e18fc855..efa39eab0256a 100644 --- a/Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml @@ -48,6 +48,9 @@ properties: interrupt-names: const: macirq + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/MAINTAINERS b/MAINTAINERS index 454b8ed119e9e..c0b3bc5a077dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21428,6 +21428,7 @@ L: netdev@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/qcom,ethqos.yaml +F: Documentation/devicetree/bindings/net/qcom,sa8255p-ethqos.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c QUALCOMM FASTRPC DRIVER From 58ccab2142204f43899233dcca6752dd226a1869 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:17 +0100 Subject: [PATCH 165/214] net: stmmac: qcom-ethqos: use generic device properties In order to drop the dependency on CONFIG_OF, convert all device property getters from OF-specific to generic device properties and stop pulling in any linux/of.h symbols. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 907fe2e927f01..1310312e3e099 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -135,7 +135,7 @@ config DWMAC_MESON config DWMAC_QCOM_ETHQOS tristate "Qualcomm ETHQOS support" default ARCH_QCOM - depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on ARCH_QCOM || COMPILE_TEST help Support for the Qualcomm ETHQOS core. diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 0826a7bd32ff5..457748e578123 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018-19, Linaro Limited +#include #include -#include #include #include #include @@ -723,7 +723,6 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv) static int qcom_ethqos_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; const struct ethqos_emac_driver_data *data; struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; @@ -774,7 +773,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ethqos->mac_base = stmmac_res.addr; - data = of_device_get_match_data(dev); + data = device_get_match_data(dev); ethqos->por = data->por; ethqos->num_por = data->num_por; ethqos->rgmii_config_loopback_en = data->rgmii_config_loopback_en; @@ -811,9 +810,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ethqos->has_emac_ge_3) plat_dat->dwmac4_addrs = &data->dwmac4_addrs; plat_dat->pmt = 1; - if (of_property_read_bool(np, "snps,tso")) + if (device_property_present(dev, "snps,tso")) plat_dat->flags |= STMMAC_FLAG_TSO_EN; - if (of_device_is_compatible(np, "qcom,qcs404-ethqos")) + if (device_is_compatible(dev, "qcom,qcs404-ethqos")) plat_dat->flags |= STMMAC_FLAG_RX_CLK_RUNS_IN_LPI; if (data->dma_addr_width) plat_dat->host_dma_width = data->dma_addr_width; From ae827eb46237338654e66466abe4b806c3cd8d0e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:18 +0100 Subject: [PATCH 166/214] net: stmmac: qcom-ethqos: wrap emac driver data in additional structure As the first step in enabling power domain support in the driver, we'll split the device match data and runtime data structures into their general and power-management-specific parts. To allow that: first wrap the emac driver data in another layer which will later be expanded. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 457748e578123..e0f0eca943420 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -97,6 +97,10 @@ struct ethqos_emac_driver_data { bool needs_sgmii_loopback; }; +struct ethqos_emac_match_data { + const struct ethqos_emac_driver_data *drv_data; +}; + struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; @@ -225,6 +229,10 @@ static const struct ethqos_emac_driver_data emac_v2_3_0_data = { .has_emac_ge_3 = false, }; +static const struct ethqos_emac_match_data emac_qcs404_data = { + .drv_data = &emac_v2_3_0_data, +}; + static const struct ethqos_emac_por emac_v2_1_0_por[] = { { .offset = RGMII_IO_MACRO_CONFIG, .value = 0x40C01343 }, { .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642C }, @@ -241,6 +249,10 @@ static const struct ethqos_emac_driver_data emac_v2_1_0_data = { .has_emac_ge_3 = false, }; +static const struct ethqos_emac_match_data emac_sm8150_data = { + .drv_data = &emac_v2_1_0_data, +}; + static const struct ethqos_emac_por emac_v3_0_0_por[] = { { .offset = RGMII_IO_MACRO_CONFIG, .value = 0x40c01343 }, { .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642c }, @@ -273,6 +285,10 @@ static const struct ethqos_emac_driver_data emac_v3_0_0_data = { }, }; +static const struct ethqos_emac_match_data emac_sc8280xp_data = { + .drv_data = &emac_v3_0_0_data, +}; + static const struct ethqos_emac_por emac_v4_0_0_por[] = { { .offset = RGMII_IO_MACRO_CONFIG, .value = 0x40c01343 }, { .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642c }, @@ -308,6 +324,10 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = { }, }; +static const struct ethqos_emac_match_data emac_sa8775p_data = { + .drv_data = &emac_v4_0_0_data, +}; + static int ethqos_dll_configure(struct qcom_ethqos *ethqos) { struct device *dev = ðqos->pdev->dev; @@ -723,7 +743,8 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv) static int qcom_ethqos_probe(struct platform_device *pdev) { - const struct ethqos_emac_driver_data *data; + const struct ethqos_emac_driver_data *drv_data; + const struct ethqos_emac_match_data *data; struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; struct device *dev = &pdev->dev; @@ -774,13 +795,15 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ethqos->mac_base = stmmac_res.addr; data = device_get_match_data(dev); - ethqos->por = data->por; - ethqos->num_por = data->num_por; - ethqos->rgmii_config_loopback_en = data->rgmii_config_loopback_en; - ethqos->has_emac_ge_3 = data->has_emac_ge_3; - ethqos->needs_sgmii_loopback = data->needs_sgmii_loopback; + drv_data = data->drv_data; + + ethqos->por = drv_data->por; + ethqos->num_por = drv_data->num_por; + ethqos->rgmii_config_loopback_en = drv_data->rgmii_config_loopback_en; + ethqos->has_emac_ge_3 = drv_data->has_emac_ge_3; + ethqos->needs_sgmii_loopback = drv_data->needs_sgmii_loopback; - ethqos->link_clk = devm_clk_get(dev, data->link_clk_name ?: "rgmii"); + ethqos->link_clk = devm_clk_get(dev, drv_data->link_clk_name ?: "rgmii"); if (IS_ERR(ethqos->link_clk)) return dev_err_probe(dev, PTR_ERR(ethqos->link_clk), "Failed to get link_clk\n"); @@ -808,14 +831,14 @@ static int qcom_ethqos_probe(struct platform_device *pdev) plat_dat->ptp_clk_freq_config = ethqos_ptp_clk_freq_config; plat_dat->core_type = DWMAC_CORE_GMAC4; if (ethqos->has_emac_ge_3) - plat_dat->dwmac4_addrs = &data->dwmac4_addrs; + plat_dat->dwmac4_addrs = &drv_data->dwmac4_addrs; plat_dat->pmt = 1; if (device_property_present(dev, "snps,tso")) plat_dat->flags |= STMMAC_FLAG_TSO_EN; if (device_is_compatible(dev, "qcom,qcs404-ethqos")) plat_dat->flags |= STMMAC_FLAG_RX_CLK_RUNS_IN_LPI; - if (data->dma_addr_width) - plat_dat->host_dma_width = data->dma_addr_width; + if (drv_data->dma_addr_width) + plat_dat->host_dma_width = drv_data->dma_addr_width; if (ethqos->serdes_phy) { plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup; @@ -830,10 +853,10 @@ static int qcom_ethqos_probe(struct platform_device *pdev) } static const struct of_device_id qcom_ethqos_match[] = { - { .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data}, - { .compatible = "qcom,sa8775p-ethqos", .data = &emac_v4_0_0_data}, - { .compatible = "qcom,sc8280xp-ethqos", .data = &emac_v3_0_0_data}, - { .compatible = "qcom,sm8150-ethqos", .data = &emac_v2_1_0_data}, + { .compatible = "qcom,qcs404-ethqos", .data = &emac_qcs404_data}, + { .compatible = "qcom,sa8775p-ethqos", .data = &emac_sa8775p_data}, + { .compatible = "qcom,sc8280xp-ethqos", .data = &emac_sc8280xp_data}, + { .compatible = "qcom,sm8150-ethqos", .data = &emac_sm8150_data}, { } }; MODULE_DEVICE_TABLE(of, qcom_ethqos_match); From ef8b3dd3bf97a2b76c052cb3f9eaf23f4471c52e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:19 +0100 Subject: [PATCH 167/214] net: stmmac: qcom-ethqos: split power management fields into a separate structure Now that we have a separate wrapper for device match data, let's extend this structure with a pointer to the structure containing fields related to power-management only. This is done because a device may have the same device settings but different power management mode (e.g.: firmware vs manual). Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index e0f0eca943420..54458ff5c9108 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -91,14 +91,18 @@ struct ethqos_emac_driver_data { unsigned int num_por; bool rgmii_config_loopback_en; bool has_emac_ge_3; - const char *link_clk_name; u32 dma_addr_width; struct dwmac4_addrs dwmac4_addrs; bool needs_sgmii_loopback; }; +struct ethqos_emac_pm_data { + const char *link_clk_name; +}; + struct ethqos_emac_match_data { const struct ethqos_emac_driver_data *drv_data; + const struct ethqos_emac_pm_data *pm_data; }; struct qcom_ethqos { @@ -303,7 +307,6 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = { .num_por = ARRAY_SIZE(emac_v4_0_0_por), .rgmii_config_loopback_en = false, .has_emac_ge_3 = true, - .link_clk_name = "phyaux", .needs_sgmii_loopback = true, .dma_addr_width = 36, .dwmac4_addrs = { @@ -324,8 +327,13 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = { }, }; +static const struct ethqos_emac_pm_data emac_sa8775p_pm_data = { + .link_clk_name = "phyaux", +}; + static const struct ethqos_emac_match_data emac_sa8775p_data = { .drv_data = &emac_v4_0_0_data, + .pm_data = &emac_sa8775p_pm_data, }; static int ethqos_dll_configure(struct qcom_ethqos *ethqos) @@ -744,11 +752,13 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv) static int qcom_ethqos_probe(struct platform_device *pdev) { const struct ethqos_emac_driver_data *drv_data; + const struct ethqos_emac_pm_data *pm_data; const struct ethqos_emac_match_data *data; struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; struct device *dev = &pdev->dev; struct qcom_ethqos *ethqos; + const char *clk_name; int ret, i; ret = stmmac_get_platform_resources(pdev, &stmmac_res); @@ -796,6 +806,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) data = device_get_match_data(dev); drv_data = data->drv_data; + pm_data = data->pm_data; + clk_name = pm_data && pm_data->link_clk_name ? + pm_data->link_clk_name : "rgmii"; ethqos->por = drv_data->por; ethqos->num_por = drv_data->num_por; @@ -803,7 +816,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ethqos->has_emac_ge_3 = drv_data->has_emac_ge_3; ethqos->needs_sgmii_loopback = drv_data->needs_sgmii_loopback; - ethqos->link_clk = devm_clk_get(dev, drv_data->link_clk_name ?: "rgmii"); + ethqos->link_clk = devm_clk_get(dev, clk_name); if (IS_ERR(ethqos->link_clk)) return dev_err_probe(dev, PTR_ERR(ethqos->link_clk), "Failed to get link_clk\n"); From a9264adf12a26b99a14e412ae117455a4b0dd432 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:20 +0100 Subject: [PATCH 168/214] net: stmmac: qcom-ethqos: split power management context into a separate struct With match data split into general and power-management sections, let's now do the same with runtime device data. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 54458ff5c9108..856fa2c7c896e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -105,17 +105,21 @@ struct ethqos_emac_match_data { const struct ethqos_emac_pm_data *pm_data; }; +struct ethqos_emac_pm_ctx { + struct clk *link_clk; + unsigned int link_clk_rate; + struct phy *serdes_phy; +}; + struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; void __iomem *mac_base; int (*configure_func)(struct qcom_ethqos *ethqos, int speed); - unsigned int link_clk_rate; - struct clk *link_clk; - struct phy *serdes_phy; - int serdes_speed; + struct ethqos_emac_pm_ctx pm; phy_interface_t phy_mode; + int serdes_speed; const struct ethqos_emac_por *por; unsigned int num_por; @@ -193,9 +197,9 @@ ethqos_update_link_clk(struct qcom_ethqos *ethqos, int speed) rate = rgmii_clock(speed); if (rate > 0) - ethqos->link_clk_rate = rate * 2; + ethqos->pm.link_clk_rate = rate * 2; - clk_set_rate(ethqos->link_clk, ethqos->link_clk_rate); + clk_set_rate(ethqos->pm.link_clk, ethqos->pm.link_clk_rate); } static void @@ -616,7 +620,7 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed) static void ethqos_set_serdes_speed(struct qcom_ethqos *ethqos, int speed) { if (ethqos->serdes_speed != speed) { - phy_set_speed(ethqos->serdes_phy, speed); + phy_set_speed(ethqos->pm.serdes_phy, speed); ethqos->serdes_speed = speed; } } @@ -683,23 +687,23 @@ static int qcom_ethqos_serdes_powerup(struct net_device *ndev, void *priv) struct qcom_ethqos *ethqos = priv; int ret; - ret = phy_init(ethqos->serdes_phy); + ret = phy_init(ethqos->pm.serdes_phy); if (ret) return ret; - ret = phy_power_on(ethqos->serdes_phy); + ret = phy_power_on(ethqos->pm.serdes_phy); if (ret) return ret; - return phy_set_speed(ethqos->serdes_phy, ethqos->serdes_speed); + return phy_set_speed(ethqos->pm.serdes_phy, ethqos->serdes_speed); } static void qcom_ethqos_serdes_powerdown(struct net_device *ndev, void *priv) { struct qcom_ethqos *ethqos = priv; - phy_power_off(ethqos->serdes_phy); - phy_exit(ethqos->serdes_phy); + phy_power_off(ethqos->pm.serdes_phy); + phy_exit(ethqos->pm.serdes_phy); } static int ethqos_clks_config(void *priv, bool enabled) @@ -708,7 +712,7 @@ static int ethqos_clks_config(void *priv, bool enabled) int ret = 0; if (enabled) { - ret = clk_prepare_enable(ethqos->link_clk); + ret = clk_prepare_enable(ethqos->pm.link_clk); if (ret) { dev_err(ðqos->pdev->dev, "link_clk enable failed\n"); return ret; @@ -721,7 +725,7 @@ static int ethqos_clks_config(void *priv, bool enabled) */ ethqos_set_func_clk_en(ethqos); } else { - clk_disable_unprepare(ethqos->link_clk); + clk_disable_unprepare(ethqos->pm.link_clk); } return ret; @@ -816,9 +820,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ethqos->has_emac_ge_3 = drv_data->has_emac_ge_3; ethqos->needs_sgmii_loopback = drv_data->needs_sgmii_loopback; - ethqos->link_clk = devm_clk_get(dev, clk_name); - if (IS_ERR(ethqos->link_clk)) - return dev_err_probe(dev, PTR_ERR(ethqos->link_clk), + ethqos->pm.link_clk = devm_clk_get(dev, clk_name); + if (IS_ERR(ethqos->pm.link_clk)) + return dev_err_probe(dev, PTR_ERR(ethqos->pm.link_clk), "Failed to get link_clk\n"); ret = ethqos_clks_config(ethqos, true); @@ -829,9 +833,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ret) return ret; - ethqos->serdes_phy = devm_phy_optional_get(dev, "serdes"); - if (IS_ERR(ethqos->serdes_phy)) - return dev_err_probe(dev, PTR_ERR(ethqos->serdes_phy), + ethqos->pm.serdes_phy = devm_phy_optional_get(dev, "serdes"); + if (IS_ERR(ethqos->pm.serdes_phy)) + return dev_err_probe(dev, PTR_ERR(ethqos->pm.serdes_phy), "Failed to get serdes phy\n"); ethqos->serdes_speed = SPEED_1000; @@ -853,7 +857,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (drv_data->dma_addr_width) plat_dat->host_dma_width = drv_data->dma_addr_width; - if (ethqos->serdes_phy) { + if (ethqos->pm.serdes_phy) { plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup; plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown; } From 4ad594389d20371a9fda76594fdb7b0c8595f61e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:21 +0100 Subject: [PATCH 169/214] net: stmmac: qcom-ethqos: define a callback for setting the serdes speed Ahead of adding support for firmware driven power management, let's allow different ways of setting the PHY speed. To that end create a callback with a single implementation for now, that will be extended later. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 856fa2c7c896e..8ba57bba3f2ee 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -120,6 +120,7 @@ struct qcom_ethqos { struct ethqos_emac_pm_ctx pm; phy_interface_t phy_mode; int serdes_speed; + int (*set_serdes_speed)(struct qcom_ethqos *ethqos); const struct ethqos_emac_por *por; unsigned int num_por; @@ -617,11 +618,16 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed) return 0; } +static int ethqos_set_serdes_speed_phy(struct qcom_ethqos *ethqos) +{ + return phy_set_speed(ethqos->pm.serdes_phy, ethqos->serdes_speed); +} + static void ethqos_set_serdes_speed(struct qcom_ethqos *ethqos, int speed) { if (ethqos->serdes_speed != speed) { - phy_set_speed(ethqos->pm.serdes_phy, speed); ethqos->serdes_speed = speed; + ethqos->set_serdes_speed(ethqos); } } @@ -838,6 +844,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ethqos->pm.serdes_phy), "Failed to get serdes phy\n"); + ethqos->set_serdes_speed = ethqos_set_serdes_speed_phy; ethqos->serdes_speed = SPEED_1000; ethqos_update_link_clk(ethqos, SPEED_1000); ethqos_set_func_clk_en(ethqos); From f708d083705c499c6fae486cc905548c415e886d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Dec 2025 12:42:22 +0100 Subject: [PATCH 170/214] net: stmmac: qcom-ethqos: add support for sa8255p Extend the driver to support a new model - sa8255p. Unlike the previously supported variants, this one's power management is done in the firmware using SCMI. This is modeled in linux using power domains so add support for them. Signed-off-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: NipaLocal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 234 ++++++++++++++++-- 1 file changed, 209 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 8ba57bba3f2ee..54f8ef3cfd7d5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "stmmac.h" #include "stmmac_platform.h" @@ -81,6 +83,13 @@ #define SGMII_10M_RX_CLK_DVDR 0x31 +enum ethqos_pd_selector { + ETHQOS_PD_CORE = 0, + ETHQOS_PD_MDIO, + ETHQOS_PD_SERDES, + ETHQOS_NUM_PDS, +}; + struct ethqos_emac_por { unsigned int offset; unsigned int value; @@ -98,6 +107,9 @@ struct ethqos_emac_driver_data { struct ethqos_emac_pm_data { const char *link_clk_name; + bool use_domains; + struct dev_pm_domain_attach_data pd; + unsigned int clk_ptp_rate; }; struct ethqos_emac_match_data { @@ -111,13 +123,20 @@ struct ethqos_emac_pm_ctx { struct phy *serdes_phy; }; +struct ethqos_emac_pd_ctx { + struct dev_pm_domain_list *pd_list; +}; + struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; void __iomem *mac_base; int (*configure_func)(struct qcom_ethqos *ethqos, int speed); - struct ethqos_emac_pm_ctx pm; + union { + struct ethqos_emac_pm_ctx pm; + struct ethqos_emac_pd_ctx pd; + }; phy_interface_t phy_mode; int serdes_speed; int (*set_serdes_speed)(struct qcom_ethqos *ethqos); @@ -341,6 +360,25 @@ static const struct ethqos_emac_match_data emac_sa8775p_data = { .pm_data = &emac_sa8775p_pm_data, }; +static const char * const emac_sa8255p_pd_names[] = { + "core", "mdio", "serdes" +}; + +static const struct ethqos_emac_pm_data emac_sa8255p_pm_data = { + .pd = { + .pd_flags = PD_FLAG_NO_DEV_LINK, + .pd_names = emac_sa8255p_pd_names, + .num_pd_names = ETHQOS_NUM_PDS, + }, + .use_domains = true, + .clk_ptp_rate = 230400000, +}; + +static const struct ethqos_emac_match_data emac_sa8255p_data = { + .drv_data = &emac_v4_0_0_data, + .pm_data = &emac_sa8255p_pm_data, +}; + static int ethqos_dll_configure(struct qcom_ethqos *ethqos) { struct device *dev = ðqos->pdev->dev; @@ -407,6 +445,28 @@ static int ethqos_dll_configure(struct qcom_ethqos *ethqos) return 0; } +static int qcom_ethqos_domain_on(struct qcom_ethqos *ethqos, + enum ethqos_pd_selector sel) +{ + struct device *dev = ethqos->pd.pd_list->pd_devs[sel]; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + dev_err(ðqos->pdev->dev, + "Failed to enable the power domain for %s\n", + dev_name(dev)); + return ret; +} + +static void qcom_ethqos_domain_off(struct qcom_ethqos *ethqos, + enum ethqos_pd_selector sel) +{ + struct device *dev = ethqos->pd.pd_list->pd_devs[sel]; + + pm_runtime_put_sync(dev); +} + static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed) { struct device *dev = ðqos->pdev->dev; @@ -623,6 +683,13 @@ static int ethqos_set_serdes_speed_phy(struct qcom_ethqos *ethqos) return phy_set_speed(ethqos->pm.serdes_phy, ethqos->serdes_speed); } +static int ethqos_set_serdes_speed_pd(struct qcom_ethqos *ethqos) +{ + struct device *dev = ethqos->pd.pd_list->pd_devs[ETHQOS_PD_SERDES]; + + return dev_pm_opp_set_level(dev, ethqos->serdes_speed); +} + static void ethqos_set_serdes_speed(struct qcom_ethqos *ethqos, int speed) { if (ethqos->serdes_speed != speed) { @@ -712,6 +779,28 @@ static void qcom_ethqos_serdes_powerdown(struct net_device *ndev, void *priv) phy_exit(ethqos->pm.serdes_phy); } +static int qcom_ethqos_pd_serdes_powerup(struct net_device *ndev, void *priv) +{ + struct qcom_ethqos *ethqos = priv; + struct device *dev = ethqos->pd.pd_list->pd_devs[ETHQOS_PD_SERDES]; + int ret; + + ret = qcom_ethqos_domain_on(ethqos, ETHQOS_PD_SERDES); + if (ret < 0) + return ret; + + return dev_pm_opp_set_level(dev, ethqos->serdes_speed); +} + +static void qcom_ethqos_pd_serdes_powerdown(struct net_device *ndev, void *priv) +{ + struct qcom_ethqos *ethqos = priv; + struct device *dev = ethqos->pd.pd_list->pd_devs[ETHQOS_PD_SERDES]; + + dev_pm_opp_set_level(dev, 0); + qcom_ethqos_domain_off(ethqos, ETHQOS_PD_SERDES); +} + static int ethqos_clks_config(void *priv, bool enabled) { struct qcom_ethqos *ethqos = priv; @@ -742,6 +831,68 @@ static void ethqos_clks_disable(void *data) ethqos_clks_config(data, false); } +static void ethqos_disable_serdes(void *data) +{ + struct qcom_ethqos *ethqos = data; + + qcom_ethqos_domain_on(ethqos, ETHQOS_PD_SERDES); +} + +static int ethqos_pd_clks_config(void *priv, bool enabled) +{ + struct qcom_ethqos *ethqos = priv; + int ret = 0; + + if (enabled) { + ret = qcom_ethqos_domain_on(ethqos, ETHQOS_PD_MDIO); + if (ret < 0) { + dev_err(ðqos->pdev->dev, + "Failed to enable the MDIO power domain\n"); + return ret; + } + + ethqos_set_func_clk_en(ethqos); + } else { + qcom_ethqos_domain_off(ethqos, ETHQOS_PD_MDIO); + } + + return ret; +} + +static int qcom_ethqos_pd_init(struct device *dev, void *priv) +{ + struct qcom_ethqos *ethqos = priv; + int ret; + + /* + * Enable functional clock to prevent DMA reset after timeout due + * to no PHY clock being enabled after the hardware block has been + * power cycled. The actual configuration will be adjusted once + * ethqos_fix_mac_speed() is called. + */ + ethqos_set_func_clk_en(ethqos); + + ret = qcom_ethqos_domain_on(ethqos, ETHQOS_PD_CORE); + if (ret) + return ret; + + ret = qcom_ethqos_domain_on(ethqos, ETHQOS_PD_MDIO); + if (ret) { + qcom_ethqos_domain_off(ethqos, ETHQOS_PD_CORE); + return ret; + } + + return 0; +} + +static void qcom_ethqos_pd_exit(struct device *dev, void *data) +{ + struct qcom_ethqos *ethqos = data; + + qcom_ethqos_domain_off(ethqos, ETHQOS_PD_MDIO); + qcom_ethqos_domain_off(ethqos, ETHQOS_PD_CORE); +} + static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv) { struct plat_stmmacenet_data *plat_dat = priv->plat; @@ -782,8 +933,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev) "dt configuration failed\n"); } - plat_dat->clks_config = ethqos_clks_config; - ethqos = devm_kzalloc(dev, sizeof(*ethqos), GFP_KERNEL); if (!ethqos) return -ENOMEM; @@ -825,28 +974,67 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ethqos->rgmii_config_loopback_en = drv_data->rgmii_config_loopback_en; ethqos->has_emac_ge_3 = drv_data->has_emac_ge_3; ethqos->needs_sgmii_loopback = drv_data->needs_sgmii_loopback; + ethqos->serdes_speed = SPEED_1000; - ethqos->pm.link_clk = devm_clk_get(dev, clk_name); - if (IS_ERR(ethqos->pm.link_clk)) - return dev_err_probe(dev, PTR_ERR(ethqos->pm.link_clk), - "Failed to get link_clk\n"); + if (pm_data && pm_data->use_domains) { + ethqos->set_serdes_speed = ethqos_set_serdes_speed_pd; - ret = ethqos_clks_config(ethqos, true); - if (ret) - return ret; + ret = devm_pm_domain_attach_list(dev, &pm_data->pd, + ðqos->pd.pd_list); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to attach power domains\n"); - ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos); - if (ret) - return ret; + plat_dat->clks_config = ethqos_pd_clks_config; + plat_dat->serdes_powerup = qcom_ethqos_pd_serdes_powerup; + plat_dat->serdes_powerdown = qcom_ethqos_pd_serdes_powerdown; + plat_dat->exit = qcom_ethqos_pd_exit; + plat_dat->init = qcom_ethqos_pd_init; + plat_dat->clk_ptp_rate = pm_data->clk_ptp_rate; - ethqos->pm.serdes_phy = devm_phy_optional_get(dev, "serdes"); - if (IS_ERR(ethqos->pm.serdes_phy)) - return dev_err_probe(dev, PTR_ERR(ethqos->pm.serdes_phy), - "Failed to get serdes phy\n"); + ret = qcom_ethqos_pd_init(dev, ethqos); + if (ret) + return ret; + + ret = qcom_ethqos_domain_on(ethqos, ETHQOS_PD_SERDES); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable the serdes power domain\n"); + + ret = devm_add_action_or_reset(dev, ethqos_disable_serdes, ethqos); + if (ret) + return ret; + } else { + ethqos->set_serdes_speed = ethqos_set_serdes_speed_phy; + + ethqos->pm.link_clk = devm_clk_get(dev, clk_name); + if (IS_ERR(ethqos->pm.link_clk)) + return dev_err_probe(dev, PTR_ERR(ethqos->pm.link_clk), + "Failed to get link_clk\n"); + + ret = ethqos_clks_config(ethqos, true); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos); + if (ret) + return ret; + + ethqos->pm.serdes_phy = devm_phy_optional_get(dev, "serdes"); + if (IS_ERR(ethqos->pm.serdes_phy)) + return dev_err_probe(dev, PTR_ERR(ethqos->pm.serdes_phy), + "Failed to get serdes phy\n"); + + ethqos_update_link_clk(ethqos, SPEED_1000); + + plat_dat->clks_config = ethqos_clks_config; + plat_dat->ptp_clk_freq_config = ethqos_ptp_clk_freq_config; + + if (ethqos->pm.serdes_phy) { + plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup; + plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown; + } + } - ethqos->set_serdes_speed = ethqos_set_serdes_speed_phy; - ethqos->serdes_speed = SPEED_1000; - ethqos_update_link_clk(ethqos, SPEED_1000); ethqos_set_func_clk_en(ethqos); plat_dat->bsp_priv = ethqos; @@ -864,11 +1052,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (drv_data->dma_addr_width) plat_dat->host_dma_width = drv_data->dma_addr_width; - if (ethqos->pm.serdes_phy) { - plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup; - plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown; - } - /* Enable TSO on queue0 and enable TBS on rest of the queues */ for (i = 1; i < plat_dat->tx_queues_to_use; i++) plat_dat->tx_queues_cfg[i].tbs_en = 1; @@ -878,6 +1061,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) static const struct of_device_id qcom_ethqos_match[] = { { .compatible = "qcom,qcs404-ethqos", .data = &emac_qcs404_data}, + { .compatible = "qcom,sa8255p-ethqos", .data = &emac_sa8255p_data}, { .compatible = "qcom,sa8775p-ethqos", .data = &emac_sa8775p_data}, { .compatible = "qcom,sc8280xp-ethqos", .data = &emac_sc8280xp_data}, { .compatible = "qcom,sm8150-ethqos", .data = &emac_sm8150_data}, From 9edc91417272dd66d0980090c82fc1faae05a86c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 19 Dec 2025 14:44:59 +0000 Subject: [PATCH 171/214] usbnet: avoid a possible crash in dql_completed() syzbot reported a crash [1] in dql_completed() after recent usbnet BQL adoption. The reason for the crash is that netdev_reset_queue() is called too soon. It should be called after cancel_work_sync(&dev->bh_work) to make sure no more TX completion can happen. [1] kernel BUG at lib/dynamic_queue_limits.c:99 ! Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 1 UID: 0 PID: 5197 Comm: udevd Tainted: G L syzkaller #0 PREEMPT(full) Tainted: [L]=SOFTLOCKUP Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 RIP: 0010:dql_completed+0xbe1/0xbf0 lib/dynamic_queue_limits.c:99 Call Trace: netdev_tx_completed_queue include/linux/netdevice.h:3864 [inline] netdev_completed_queue include/linux/netdevice.h:3894 [inline] usbnet_bh+0x793/0x1020 drivers/net/usb/usbnet.c:1601 process_one_work kernel/workqueue.c:3257 [inline] process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 bh_worker+0x2b1/0x600 kernel/workqueue.c:3611 tasklet_action+0xc/0x70 kernel/softirq.c:952 handle_softirqs+0x27d/0x850 kernel/softirq.c:622 __do_softirq kernel/softirq.c:656 [inline] invoke_softirq kernel/softirq.c:496 [inline] __irq_exit_rcu+0xca/0x1f0 kernel/softirq.c:723 irq_exit_rcu+0x9/0x30 kernel/softirq.c:739 Fixes: 7ff14c52049e ("usbnet: Add support for Byte Queue Limits (BQL)") Reported-by: syzbot+5b55e49f8bbd84631a9c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6945644f.a70a0220.207337.0113.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Cc: Simon Schippers Signed-off-by: NipaLocal --- drivers/net/usb/usbnet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 1d9faa70ba3b7..36742e64cff75 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -831,7 +831,6 @@ int usbnet_stop(struct net_device *net) clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue(net); - netdev_reset_queue(net); netif_info(dev, ifdown, dev->net, "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", @@ -875,6 +874,8 @@ int usbnet_stop(struct net_device *net) timer_delete_sync(&dev->delay); cancel_work_sync(&dev->kevent); + netdev_reset_queue(net); + if (!pm) usb_autopm_put_interface(dev->intf); From 1a4705b4408dcfec601c869f1e8daad1a4817607 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 19 Dec 2025 18:21:03 +0100 Subject: [PATCH 172/214] trace: fix UBSAN warning in __remove_instance xfs/558 triggers the following UBSAN warning: ------------[ cut here ]------------ UBSAN: shift-out-of-bounds in /storage/home/djwong/cdev/work/linux-xfs/kernel/trace/trace.c:10510:10 shift exponent 32 is too large for 32-bit type 'int' CPU: 1 UID: 0 PID: 888674 Comm: rmdir Not tainted 6.19.0-rc1-xfsx #rc1 PREEMPT(lazy) dbf607ef4c142c563f76d706e71af9731d7b9c90 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-4.module+el8.8.0+21164+ed375313 04/01/2014 Call Trace: dump_stack_lvl+0x4a/0x70 ubsan_epilogue+0x5/0x2b __ubsan_handle_shift_out_of_bounds.cold+0x5e/0x113 __remove_instance.part.0.constprop.0.cold+0x18/0x26f instance_rmdir+0xf3/0x110 tracefs_syscall_rmdir+0x4d/0x90 vfs_rmdir+0x139/0x230 do_rmdir+0x143/0x230 __x64_sys_rmdir+0x1d/0x20 do_syscall_64+0x44/0x230 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f7ae8e51f17 Code: f0 ff ff 73 01 c3 48 8b 0d de 2e 0e 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 b8 54 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 8b 15 b1 2e 0e 00 f7 d8 64 89 02 b8 RSP: 002b:00007ffd90743f08 EFLAGS: 00000246 ORIG_RAX: 0000000000000054 RAX: ffffffffffffffda RBX: 00007ffd907440f8 RCX: 00007f7ae8e51f17 RDX: 00007f7ae8f3c5c0 RSI: 00007ffd90744a21 RDI: 00007ffd90744a21 RBP: 0000000000000002 R08: 0000000000000000 R09: 0000000000000000 R10: 00007f7ae8f35ac0 R11: 0000000000000246 R12: 00007ffd90744a21 R13: 0000000000000001 R14: 00007f7ae8f8b000 R15: 000055e5283e6a98 ---[ end trace ]--- whilst tearing down an ftrace instance. TRACE_FLAGS_MAX_SIZE is now 64, so the mask comparison expression must be typecast to a u64 value to avoid an overflow. AFAICT, ZEROED_TRACE_FLAGS is already cast to ULL so this is ok. Fixes: bbec8e28cac592 ("tracing: Allow tracer to add more than 32 options") Signed-off-by: "Darrick J. Wong" Signed-off-by: Paolo Abeni Signed-off-by: NipaLocal --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e575956ef9b5a..6f2148df14d96 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -10507,7 +10507,7 @@ static int __remove_instance(struct trace_array *tr) /* Disable all the flags that were enabled coming in */ for (i = 0; i < TRACE_FLAGS_MAX_SIZE; i++) { - if ((1 << i) & ZEROED_TRACE_FLAGS) + if ((1ULL << i) & ZEROED_TRACE_FLAGS) set_tracer_flag(tr, 1ULL << i, 0); } From cbf9ca6756f168bc9070fee18342bd8f9c813a95 Mon Sep 17 00:00:00 2001 From: Will Rosenberg Date: Fri, 19 Dec 2025 10:36:37 -0700 Subject: [PATCH 173/214] ipv6: BUG() in pskb_expand_head() as part of calipso_skbuff_setattr() There exists a kernel oops caused by a BUG_ON(nhead < 0) at net/core/skbuff.c:2232 in pskb_expand_head(). This bug is triggered as part of the calipso_skbuff_setattr() routine when skb_cow() is passed headroom > INT_MAX (i.e. (int)(skb_headroom(skb) + len_delta) < 0). The root cause of the bug is due to an implicit integer cast in __skb_cow(). The check (headroom > skb_headroom(skb)) is meant to ensure that delta = headroom - skb_headroom(skb) is never negative, otherwise we will trigger a BUG_ON in pskb_expand_head(). However, if headroom > INT_MAX and delta <= -NET_SKB_PAD, the check passes, delta becomes negative, and pskb_expand_head() is passed a negative value for nhead. Fix the trigger condition in calipso_skbuff_setattr(). Avoid passing "negative" headroom sizes to skb_cow() within calipso_skbuff_setattr() by only using skb_cow() to grow headroom. PoC: Using `netlabelctl` tool: netlabelctl map del default netlabelctl calipso add pass doi:7 netlabelctl map add default address:0::1/128 protocol:calipso,7 Then run the following PoC: int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); // setup msghdr int cmsg_size = 2; int cmsg_len = 0x60; struct msghdr msg; struct sockaddr_in6 dest_addr; struct cmsghdr * cmsg = (struct cmsghdr *) calloc(1, sizeof(struct cmsghdr) + cmsg_len); msg.msg_name = &dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = NULL; msg.msg_iovlen = 0; msg.msg_control = cmsg; msg.msg_controllen = cmsg_len; msg.msg_flags = 0; // setup sockaddr dest_addr.sin6_family = AF_INET6; dest_addr.sin6_port = htons(31337); dest_addr.sin6_flowinfo = htonl(31337); dest_addr.sin6_addr = in6addr_loopback; dest_addr.sin6_scope_id = 31337; // setup cmsghdr cmsg->cmsg_len = cmsg_len; cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_HOPOPTS; char * hop_hdr = (char *)cmsg + sizeof(struct cmsghdr); hop_hdr[1] = 0x9; //set hop size - (0x9 + 1) * 8 = 80 sendmsg(fd, &msg, 0); Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.") Signed-off-by: Will Rosenberg Acked-by: Paul Moore Signed-off-by: NipaLocal --- net/ipv6/calipso.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index df1986973430c..21f6ed126253a 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -1342,7 +1342,8 @@ static int calipso_skbuff_setattr(struct sk_buff *skb, /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */ pad = ((new_end & 4) + (end & 7)) & 7; len_delta = new_end - (int)end + pad; - ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); + ret_val = skb_cow(skb, + skb_headroom(skb) + (len_delta > 0 ? len_delta : 0)); if (ret_val < 0) return ret_val; From f842dfdd61fe3511bf9d3c856adbd9ef67fd3685 Mon Sep 17 00:00:00 2001 From: Jerry Wu Date: Sat, 20 Dec 2025 19:01:23 +0000 Subject: [PATCH 174/214] net: mscc: ocelot: Fix crash when adding interface under a lag Commit 15faa1f67ab4 ("lan966x: Fix crash when adding interface under a lag") fixed a similar issue in the lan966x driver caused by a NULL pointer dereference. The ocelot_set_aggr_pgids() function in the ocelot driver has similar logic and is susceptible to the same crash. This issue specifically affects the ocelot_vsc7514.c frontend, which leaves unused ports as NULL pointers. The felix_vsc9959.c frontend is unaffected as it uses the DSA framework which registers all ports. Fix this by checking if the port pointer is valid before accessing it. Fixes: 528d3f190c98 ("net: mscc: ocelot: drop the use of the "lags" array") Signed-off-by: Jerry Wu Signed-off-by: NipaLocal --- drivers/net/ethernet/mscc/ocelot.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 08bee56aea35f..c345d9b17c892 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2307,14 +2307,16 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot) /* Now, set PGIDs for each active LAG */ for (lag = 0; lag < ocelot->num_phys_ports; lag++) { - struct net_device *bond = ocelot->ports[lag]->bond; + struct ocelot_port *ocelot_port = ocelot->ports[lag]; int num_active_ports = 0; + struct net_device *bond; unsigned long bond_mask; u8 aggr_idx[16]; - if (!bond || (visited & BIT(lag))) + if (!ocelot_port || !ocelot_port->bond || (visited & BIT(lag))) continue; + bond = ocelot_port->bond; bond_mask = ocelot_get_bond_mask(ocelot, bond); for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) { From 471923777a4b9b2a40fdc800067245735f84fc63 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sun, 21 Dec 2025 00:24:00 -0800 Subject: [PATCH 175/214] net: usb: sr9700: fix incorrect command used to write single register This fixes the device failing to initialize with "error reading MAC address" for me, probably because the incorrect write of NCR_RST to SR_NCR is not actually resetting the device. Fixes: c9b37458e95629b1d1171457afdcc1bf1eb7881d ("USB2NET : SR9700 : One chip USB 1.1 USB2NET SR9700Device Driver Support") Cc: stable@vger.kernel.org Signed-off-by: Ethan Nelson-Moore Signed-off-by: NipaLocal --- drivers/net/usb/sr9700.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index d8ffb59eaf348..820c4c5069792 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -52,7 +52,7 @@ static int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) { - return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, + return usbnet_write_cmd(dev, SR_WR_REG, SR_REQ_WR_REG, value, reg, NULL, 0); } @@ -65,7 +65,7 @@ static void sr_write_async(struct usbnet *dev, u8 reg, u16 length, static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) { - usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, + usbnet_write_cmd_async(dev, SR_WR_REG, SR_REQ_WR_REG, value, reg, NULL, 0); } From 890a147408ab011f644bfb9fd4a9b5cbdda70701 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 21 Dec 2025 16:48:28 +0200 Subject: [PATCH 176/214] ipv4: Fix reference count leak when using error routes with nexthop objects When a nexthop object is deleted, it is marked as dead and then fib_table_flush() is called to flush all the routes that are using the dead nexthop. The current logic in fib_table_flush() is to only flush error routes (e.g., blackhole) when it is called as part of network namespace dismantle (i.e., with flush_all=true). Therefore, error routes are not flushed when their nexthop object is deleted: # ip link add name dummy1 up type dummy # ip nexthop add id 1 dev dummy1 # ip route add 198.51.100.1/32 nhid 1 # ip route add blackhole 198.51.100.2/32 nhid 1 # ip nexthop del id 1 # ip route show blackhole 198.51.100.2 nhid 1 dev dummy1 As such, they keep holding a reference on the nexthop object which in turn holds a reference on the nexthop device, resulting in a reference count leak: # ip link del dev dummy1 [ 70.516258] unregister_netdevice: waiting for dummy1 to become free. Usage count = 2 Fix by flushing error routes when their nexthop is marked as dead. IPv6 does not suffer from this problem. Fixes: 493ced1ac47c ("ipv4: Allow routes to use nexthop objects") Reported-by: Tetsuo Handa Closes: https://lore.kernel.org/netdev/d943f806-4da6-4970-ac28-b9373b0e63ac@I-love.SAKURA.ne.jp/ Reported-by: syzbot+881d65229ca4f9ae8c84@syzkaller.appspotmail.com Signed-off-by: Ido Schimmel Signed-off-by: NipaLocal --- net/ipv4/fib_trie.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 59a6f0a9638f9..7e2c17fec3fc4 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2053,10 +2053,11 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) continue; } - /* Do not flush error routes if network namespace is - * not being dismantled + /* When not flushing the entire table, skip error + * routes that are not marked for deletion. */ - if (!flush_all && fib_props[fa->fa_type].error) { + if (!flush_all && fib_props[fa->fa_type].error && + !(fi->fib_flags & RTNH_F_DEAD)) { slen = fa->fa_slen; continue; } From 1e2d6333131a3579f2df24a389f66f2fd79e3f11 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 21 Dec 2025 16:48:29 +0200 Subject: [PATCH 177/214] selftests: fib_nexthops: Add test cases for error routes deletion Add test cases that check that error routes (e.g., blackhole) are deleted when their nexthop is deleted. Output without "ipv4: Fix reference count leak when using error routes with nexthop objects": # ./fib_nexthops.sh -t "ipv4_fcnal ipv6_fcnal" IPv4 functional ---------------------- [...] WARNING: Unexpected route entry TEST: Error route removed on nexthop deletion [FAIL] IPv6 ---------------------- [...] TEST: Error route removed on nexthop deletion [ OK ] Tests passed: 20 Tests failed: 1 Tests skipped: 0 Output with "ipv4: Fix reference count leak when using error routes with nexthop objects": # ./fib_nexthops.sh -t "ipv4_fcnal ipv6_fcnal" IPv4 functional ---------------------- [...] TEST: Error route removed on nexthop deletion [ OK ] IPv6 ---------------------- [...] TEST: Error route removed on nexthop deletion [ OK ] Tests passed: 21 Tests failed: 0 Tests skipped: 0 Signed-off-by: Ido Schimmel Signed-off-by: NipaLocal --- tools/testing/selftests/net/fib_nexthops.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index 2b0a90581e2f1..21026b6676670 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -800,6 +800,14 @@ ipv6_fcnal() set +e check_nexthop "dev veth1" "" log_test $? 0 "Nexthops removed on admin down" + + # error routes should be deleted when their nexthop is deleted + run_cmd "$IP li set dev veth1 up" + run_cmd "$IP -6 nexthop add id 58 dev veth1" + run_cmd "$IP ro add blackhole 2001:db8:101::1/128 nhid 58" + run_cmd "$IP nexthop del id 58" + check_route6 "2001:db8:101::1" "" + log_test $? 0 "Error route removed on nexthop deletion" } ipv6_grp_refs() @@ -1459,6 +1467,13 @@ ipv4_fcnal() run_cmd "$IP ro del 172.16.102.0/24" log_test $? 0 "Delete route when not specifying nexthop attributes" + + # error routes should be deleted when their nexthop is deleted + run_cmd "$IP nexthop add id 23 dev veth1" + run_cmd "$IP ro add blackhole 172.16.102.100/32 nhid 23" + run_cmd "$IP nexthop del id 23" + check_route "172.16.102.100" "" + log_test $? 0 "Error route removed on nexthop deletion" } ipv4_grp_fcnal() From 9f2bfcfff96eecb57da9787be347c0276c550ffa Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Sun, 21 Dec 2025 19:26:38 +0000 Subject: [PATCH 178/214] net: fib: restore ECMP balance from loopback Preference of nexthop with source address broke ECMP for packets with source addresses which are not in the broadcast domain, but rather added to loopback/dummy interfaces. Original behaviour was to balance over nexthops while now it uses the latest nexthop from the group. To fix the issue introduce next hop scoring system where next hops with source address equal to requested will always have higher priority. For the case with 198.51.100.1/32 assigned to dummy0 and routed using 192.0.2.0/24 and 203.0.113.0/24 networks: 2: dummy0: mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether d6:54:8a:ff:78:f5 brd ff:ff:ff:ff:ff:ff inet 198.51.100.1/32 scope global dummy0 valid_lft forever preferred_lft forever 7: veth1@if6: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 06:ed:98:87:6d:8a brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.0.2.2/24 scope global veth1 valid_lft forever preferred_lft forever inet6 fe80::4ed:98ff:fe87:6d8a/64 scope link proto kernel_ll valid_lft forever preferred_lft forever 9: veth3@if8: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether ae:75:23:38:a0:d2 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 203.0.113.2/24 scope global veth3 valid_lft forever preferred_lft forever inet6 fe80::ac75:23ff:fe38:a0d2/64 scope link proto kernel_ll valid_lft forever preferred_lft forever ~ ip ro list: default nexthop via 192.0.2.1 dev veth1 weight 1 nexthop via 203.0.113.1 dev veth3 weight 1 192.0.2.0/24 dev veth1 proto kernel scope link src 192.0.2.2 203.0.113.0/24 dev veth3 proto kernel scope link src 203.0.113.2 before: for i in {1..255} ; do ip ro get 10.0.0.$i; done | grep veth | awk ' {print $(NF-2)}' | sort | uniq -c: 255 veth3 after: for i in {1..255} ; do ip ro get 10.0.0.$i; done | grep veth | awk ' {print $(NF-2)}' | sort | uniq -c: 122 veth1 133 veth3 Fixes: 32607a332cfe ("ipv4: prefer multipath nexthop that matches source address") Signed-off-by: Vadim Fedorenko Reviewed-by: Ido Schimmel Reviewed-by: Willem de Bruijn Signed-off-by: NipaLocal --- net/ipv4/fib_semantics.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index a5f3c8459758f..0caf38e44c738 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -2167,8 +2167,8 @@ void fib_select_multipath(struct fib_result *res, int hash, { struct fib_info *fi = res->fi; struct net *net = fi->fib_net; - bool found = false; bool use_neigh; + int score = -1; __be32 saddr; if (unlikely(res->fi->nh)) { @@ -2180,7 +2180,7 @@ void fib_select_multipath(struct fib_result *res, int hash, saddr = fl4 ? fl4->saddr : 0; change_nexthops(fi) { - int nh_upper_bound; + int nh_upper_bound, nh_score = 0; /* Nexthops without a carrier are assigned an upper bound of * minus one when "ignore_routes_with_linkdown" is set. @@ -2190,24 +2190,18 @@ void fib_select_multipath(struct fib_result *res, int hash, (use_neigh && !fib_good_nh(nexthop_nh))) continue; - if (!found) { + if (saddr && nexthop_nh->nh_saddr == saddr) + nh_score += 2; + if (hash <= nh_upper_bound) + nh_score++; + if (score < nh_score) { res->nh_sel = nhsel; res->nhc = &nexthop_nh->nh_common; - found = !saddr || nexthop_nh->nh_saddr == saddr; + if (nh_score == 3 || (!saddr && nh_score == 1)) + return; + score = nh_score; } - if (hash > nh_upper_bound) - continue; - - if (!saddr || nexthop_nh->nh_saddr == saddr) { - res->nh_sel = nhsel; - res->nhc = &nexthop_nh->nh_common; - return; - } - - if (found) - return; - } endfor_nexthops(fi); } #endif From 660bc162d7a29c89b0aff3b6463e75f17846e082 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Sun, 21 Dec 2025 19:26:39 +0000 Subject: [PATCH 179/214] selftests: fib_test: Add test case for ipv4 multi nexthops The test checks that with multi nexthops route the preferred route is the one which matches source ip. In case when source ip is on dummy interface, it checks that the routes are balanced. Reviewed-by: Willem de Bruijn Signed-off-by: Vadim Fedorenko Signed-off-by: NipaLocal --- tools/testing/selftests/net/fib_tests.sh | 70 +++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index a88f797c549a7..c5694cc4ddd26 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -12,7 +12,7 @@ TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify \ ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr \ ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test \ ipv4_mpath_list ipv6_mpath_list ipv4_mpath_balance ipv6_mpath_balance \ - fib6_ra_to_static" + ipv4_mpath_balance_preferred fib6_ra_to_static" VERBOSE=0 PAUSE_ON_FAIL=no @@ -2751,6 +2751,73 @@ ipv4_mpath_balance_test() forwarding_cleanup } +get_route_dev_src() +{ + local pfx="$1" + local src="$2" + local out + + if out=$($IP -j route get "$pfx" from "$src" | jq -re ".[0].dev"); then + echo "$out" + fi +} + +ipv4_mpath_preferred() +{ + local src_ip=$1 + local pref_dev=$2 + local dev routes + local route0=0 + local route1=0 + local pref_route=0 + num_routes=254 + + for i in $(seq 1 $num_routes) ; do + dev=$(get_route_dev_src 172.16.105.$i $src_ip) + if [ "$dev" = "$pref_dev" ]; then + pref_route=$((pref_route+1)) + elif [ "$dev" = "veth1" ]; then + route0=$((route0+1)) + elif [ "$dev" = "veth3" ]; then + route1=$((route1+1)) + fi + done + + routes=$((route0+route1)) + + [ "$VERBOSE" = "1" ] && echo "multipath: routes seen: ($route0,$route1,$pref_route)" + + if [ x"$pref_dev" = x"" ]; then + [[ $routes -ge $num_routes ]] && [[ $route0 -gt 0 ]] && [[ $route1 -gt 0 ]] + else + [[ $pref_route -ge $num_routes ]] + fi + +} + +ipv4_mpath_balance_preferred_test() +{ + echo + echo "IPv4 multipath load balance preferred route" + + forwarding_setup + + $IP route add 172.16.105.0/24 \ + nexthop via 172.16.101.2 \ + nexthop via 172.16.103.2 + + ipv4_mpath_preferred 172.16.101.1 veth1 + log_test $? 0 "IPv4 multipath loadbalance from veth1" + + ipv4_mpath_preferred 172.16.103.1 veth3 + log_test $? 0 "IPv4 multipath loadbalance from veth3" + + ipv4_mpath_preferred 198.51.100.1 + log_test $? 0 "IPv4 multipath loadbalance from dummy" + + forwarding_cleanup +} + ipv6_mpath_balance_test() { echo @@ -2861,6 +2928,7 @@ do ipv6_mpath_list) ipv6_mpath_list_test;; ipv4_mpath_balance) ipv4_mpath_balance_test;; ipv6_mpath_balance) ipv6_mpath_balance_test;; + ipv4_mpath_balance_preferred) ipv4_mpath_balance_preferred_test;; fib6_ra_to_static) fib6_ra_to_static;; help) echo "Test names: $TESTS"; exit 0;; From 7d3cb73d234aae08f187cdc02a2b27c3b99c6bae Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Mon, 22 Dec 2025 09:56:24 +0800 Subject: [PATCH 180/214] net: macb: Relocate mog_init_rings() callback from macb_mac_link_up() to macb_open() In the non-RT kernel, local_bh_disable() merely disables preemption, whereas it maps to an actual spin lock in the RT kernel. Consequently, when attempting to refill RX buffers via netdev_alloc_skb() in macb_mac_link_up(), a deadlock scenario arises as follows: WARNING: possible circular locking dependency detected 6.18.0-08691-g2061f18ad76e #39 Not tainted ------------------------------------------------------ kworker/0:0/8 is trying to acquire lock: ffff00080369bbe0 (&bp->lock){+.+.}-{3:3}, at: macb_start_xmit+0x808/0xb7c but task is already holding lock: ffff000803698e58 (&queue->tx_ptr_lock){+...}-{3:3}, at: macb_start_xmit +0x148/0xb7c which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #3 (&queue->tx_ptr_lock){+...}-{3:3}: rt_spin_lock+0x50/0x1f0 macb_start_xmit+0x148/0xb7c dev_hard_start_xmit+0x94/0x284 sch_direct_xmit+0x8c/0x37c __dev_queue_xmit+0x708/0x1120 neigh_resolve_output+0x148/0x28c ip6_finish_output2+0x2c0/0xb2c __ip6_finish_output+0x114/0x308 ip6_output+0xc4/0x4a4 mld_sendpack+0x220/0x68c mld_ifc_work+0x2a8/0x4f4 process_one_work+0x20c/0x5f8 worker_thread+0x1b0/0x35c kthread+0x144/0x200 ret_from_fork+0x10/0x20 -> #2 (_xmit_ETHER#2){+...}-{3:3}: rt_spin_lock+0x50/0x1f0 sch_direct_xmit+0x11c/0x37c __dev_queue_xmit+0x708/0x1120 neigh_resolve_output+0x148/0x28c ip6_finish_output2+0x2c0/0xb2c __ip6_finish_output+0x114/0x308 ip6_output+0xc4/0x4a4 mld_sendpack+0x220/0x68c mld_ifc_work+0x2a8/0x4f4 process_one_work+0x20c/0x5f8 worker_thread+0x1b0/0x35c kthread+0x144/0x200 ret_from_fork+0x10/0x20 -> #1 ((softirq_ctrl.lock)){+.+.}-{3:3}: lock_release+0x250/0x348 __local_bh_enable_ip+0x7c/0x240 __netdev_alloc_skb+0x1b4/0x1d8 gem_rx_refill+0xdc/0x240 gem_init_rings+0xb4/0x108 macb_mac_link_up+0x9c/0x2b4 phylink_resolve+0x170/0x614 process_one_work+0x20c/0x5f8 worker_thread+0x1b0/0x35c kthread+0x144/0x200 ret_from_fork+0x10/0x20 -> #0 (&bp->lock){+.+.}-{3:3}: __lock_acquire+0x15a8/0x2084 lock_acquire+0x1cc/0x350 rt_spin_lock+0x50/0x1f0 macb_start_xmit+0x808/0xb7c dev_hard_start_xmit+0x94/0x284 sch_direct_xmit+0x8c/0x37c __dev_queue_xmit+0x708/0x1120 neigh_resolve_output+0x148/0x28c ip6_finish_output2+0x2c0/0xb2c __ip6_finish_output+0x114/0x308 ip6_output+0xc4/0x4a4 mld_sendpack+0x220/0x68c mld_ifc_work+0x2a8/0x4f4 process_one_work+0x20c/0x5f8 worker_thread+0x1b0/0x35c kthread+0x144/0x200 ret_from_fork+0x10/0x20 other info that might help us debug this: Chain exists of: &bp->lock --> _xmit_ETHER#2 --> &queue->tx_ptr_lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&queue->tx_ptr_lock); lock(_xmit_ETHER#2); lock(&queue->tx_ptr_lock); lock(&bp->lock); *** DEADLOCK *** Call trace: show_stack+0x18/0x24 (C) dump_stack_lvl+0xa0/0xf0 dump_stack+0x18/0x24 print_circular_bug+0x28c/0x370 check_noncircular+0x198/0x1ac __lock_acquire+0x15a8/0x2084 lock_acquire+0x1cc/0x350 rt_spin_lock+0x50/0x1f0 macb_start_xmit+0x808/0xb7c dev_hard_start_xmit+0x94/0x284 sch_direct_xmit+0x8c/0x37c __dev_queue_xmit+0x708/0x1120 neigh_resolve_output+0x148/0x28c ip6_finish_output2+0x2c0/0xb2c __ip6_finish_output+0x114/0x308 ip6_output+0xc4/0x4a4 mld_sendpack+0x220/0x68c mld_ifc_work+0x2a8/0x4f4 process_one_work+0x20c/0x5f8 worker_thread+0x1b0/0x35c kthread+0x144/0x200 ret_from_fork+0x10/0x20 Notably, invoking the mog_init_rings() callback upon link establishment is unnecessary. Instead, we can exclusively call mog_init_rings() within the ndo_open() callback. This adjustment resolves the deadlock issue. Furthermore, since MACB_CAPS_MACB_IS_EMAC cases do not use mog_init_rings() when opening the network interface via at91ether_open(), moving mog_init_rings() to macb_open() also eliminates the MACB_CAPS_MACB_IS_EMAC check. Fixes: 633e98a711ac ("net: macb: use resolved link config in mac_link_up()") Cc: stable@vger.kernel.org Suggested-by: Kevin Hao Signed-off-by: Xiaolei Wang Signed-off-by: NipaLocal --- drivers/net/ethernet/cadence/macb_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index e461f5072884e..6511ecd5856bd 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -708,7 +708,6 @@ static void macb_mac_link_up(struct phylink_config *config, /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ - bp->macbgem_ops.mog_init_rings(bp); macb_init_buffers(bp); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2954,6 +2953,8 @@ static int macb_open(struct net_device *dev) goto pm_exit; } + bp->macbgem_ops.mog_init_rings(bp); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { napi_enable(&queue->napi_rx); napi_enable(&queue->napi_tx); From c9bc8bdd7be96a872f3c0b703e5890cb4e03645c Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Mon, 22 Dec 2025 10:26:28 +0800 Subject: [PATCH 181/214] net: enetc: do not print error log if addr is 0 A value of 0 for addr indicates that the IEB_LBCR register does not need to be configured, as its default value is 0. However, the driver will print an error log if addr is 0, so this issue needs to be fixed. Fixes: 50bfd9c06f0f ("net: enetc: set external PHY address in IERB for i.MX94 ENETC") Signed-off-by: Wei Fang Signed-off-by: NipaLocal --- drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c index 443983fdecd95..7fd39f8952901 100644 --- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c +++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c @@ -577,11 +577,17 @@ static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv, } addr = netc_get_phy_addr(np); - if (addr <= 0) { + if (addr < 0) { dev_err(dev, "Failed to get PHY address\n"); return addr; } + /* The default value of LaBCR[MDIO_PHYAD_PRTAD] is 0, + * so no need to set the register. + */ + if (!addr) + return 0; + if (phy_mask & BIT(addr)) { dev_err(dev, "Find same PHY address in EMDIO and ENETC node\n"); From 9493cfeb046e91f739447dee69320a7be278d511 Mon Sep 17 00:00:00 2001 From: Manas Date: Mon, 22 Dec 2025 11:43:06 +0530 Subject: [PATCH 182/214] net/sched: Fix divide error in tabledist Previously, a duplication check was added to ensure that a duplicating netem cannot exist in a tree with other netems. When check_netem_in_tree() fails after parameter updates, the qdisc structure is left in an inconsistent state with some new values applied but duplicate not updated. Move the tree validation check before modifying any qdisc parameters v1 -> v2: Fix whitespace Signed-off-by: NipaLocal --- net/sched/sch_netem.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 32a5f33040461..1a2b498ada839 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -1055,6 +1055,11 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, q->loss_model = CLG_RANDOM; } + ret = check_netem_in_tree(sch, qopt->duplicate, extack); + if (ret) + goto unlock; + q->duplicate = qopt->duplicate; + if (delay_dist) swap(q->delay_dist, delay_dist); if (slot_dist) @@ -1068,12 +1073,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, q->counter = 0; q->loss = qopt->loss; - ret = check_netem_in_tree(sch, qopt->duplicate, extack); - if (ret) - goto unlock; - - q->duplicate = qopt->duplicate; - /* for compatibility with earlier versions. * if gap is set, need to assume 100% probability */ From f749d7b91bd1b4bf31cb83a2d568d081d51831ea Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:32:27 +0100 Subject: [PATCH 183/214] rust: net: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Signed-off-by: NipaLocal --- rust/kernel/net/phy.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index bf6272d87a7b4..3ca99db5cccf2 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -777,7 +777,6 @@ impl DeviceMask { /// /// ``` /// # mod module_phy_driver_sample { -/// use kernel::c_str; /// use kernel::net::phy::{self, DeviceId}; /// use kernel::prelude::*; /// @@ -796,7 +795,7 @@ impl DeviceMask { /// /// #[vtable] /// impl phy::Driver for PhySample { -/// const NAME: &'static CStr = c_str!("PhySample"); +/// const NAME: &'static CStr = c"PhySample"; /// const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x00000001); /// } /// # } @@ -805,7 +804,6 @@ impl DeviceMask { /// This expands to the following code: /// /// ```ignore -/// use kernel::c_str; /// use kernel::net::phy::{self, DeviceId}; /// use kernel::prelude::*; /// @@ -825,7 +823,7 @@ impl DeviceMask { /// /// #[vtable] /// impl phy::Driver for PhySample { -/// const NAME: &'static CStr = c_str!("PhySample"); +/// const NAME: &'static CStr = c"PhySample"; /// const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x00000001); /// } /// From d1874572091e578bb0f6e5a1a661a3b3cbf32ea0 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 22 Dec 2025 13:32:28 +0100 Subject: [PATCH 184/214] drivers: net: replace `kernel::c_str!` with C-Strings C-String literals were added in Rust 1.77. Replace instances of `kernel::c_str!` with C-String literals where possible. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Signed-off-by: NipaLocal --- drivers/net/phy/ax88796b_rust.rs | 7 +++---- drivers/net/phy/qt2025.rs | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/ax88796b_rust.rs b/drivers/net/phy/ax88796b_rust.rs index bc73ebccc2aac..2d24628a4e581 100644 --- a/drivers/net/phy/ax88796b_rust.rs +++ b/drivers/net/phy/ax88796b_rust.rs @@ -5,7 +5,6 @@ //! //! C version of this driver: [`drivers/net/phy/ax88796b.c`](./ax88796b.c) use kernel::{ - c_str, net::phy::{self, reg::C22, DeviceId, Driver}, prelude::*, uapi, @@ -41,7 +40,7 @@ struct PhyAX88772A; #[vtable] impl Driver for PhyAX88772A { const FLAGS: u32 = phy::flags::IS_INTERNAL; - const NAME: &'static CStr = c_str!("Asix Electronics AX88772A"); + const NAME: &'static CStr = c"Asix Electronics AX88772A"; const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1861); // AX88772A is not working properly with some old switches (NETGEAR EN 108TP): @@ -105,7 +104,7 @@ struct PhyAX88772C; #[vtable] impl Driver for PhyAX88772C { const FLAGS: u32 = phy::flags::IS_INTERNAL; - const NAME: &'static CStr = c_str!("Asix Electronics AX88772C"); + const NAME: &'static CStr = c"Asix Electronics AX88772C"; const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1881); fn suspend(dev: &mut phy::Device) -> Result { @@ -125,7 +124,7 @@ struct PhyAX88796B; #[vtable] impl Driver for PhyAX88796B { - const NAME: &'static CStr = c_str!("Asix Electronics AX88796B"); + const NAME: &'static CStr = c"Asix Electronics AX88796B"; const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_model_mask(0x003b1841); fn soft_reset(dev: &mut phy::Device) -> Result { diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs index aaaead6512a01..470d89a0ac006 100644 --- a/drivers/net/phy/qt2025.rs +++ b/drivers/net/phy/qt2025.rs @@ -9,7 +9,6 @@ //! //! The QT2025 PHY integrates an Intel 8051 micro-controller. -use kernel::c_str; use kernel::error::code; use kernel::firmware::Firmware; use kernel::io::poll::read_poll_timeout; @@ -38,7 +37,7 @@ struct PhyQT2025; #[vtable] impl Driver for PhyQT2025 { - const NAME: &'static CStr = c_str!("QT2025 10Gpbs SFP+"); + const NAME: &'static CStr = c"QT2025 10Gpbs SFP+"; const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x0043a400); fn probe(dev: &mut phy::Device) -> Result<()> { @@ -71,7 +70,7 @@ impl Driver for PhyQT2025 { // The micro-controller will start running from the boot ROM. dev.write(C45::new(Mmd::PCS, 0xe854), 0x00c0)?; - let fw = Firmware::request(c_str!("qt2025-2.0.3.3.fw"), dev.as_ref())?; + let fw = Firmware::request(c"qt2025-2.0.3.3.fw", dev.as_ref())?; if fw.data().len() > SZ_16K + SZ_8K { return Err(code::EFBIG); } From 9de9e6a1cc66468f534ab07b09fa810af3d069a0 Mon Sep 17 00:00:00 2001 From: Chiara Meiohas Date: Mon, 22 Dec 2025 14:40:36 +0200 Subject: [PATCH 185/214] RDMA/mlx5: Move device async_ctx initialization Move the async_ctx initialization from mlx5_mkey_cache_init() to mlx5_ib_stage_init_init() since the async_ctx is used by both the MR cache and DEVX. Also add the corresponding cleanup in mlx5_ib_stage_init_cleanup() to properly release the async_ctx resources. Signed-off-by: Chiara Meiohas Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/hw/mlx5/main.c | 3 +++ drivers/infiniband/hw/mlx5/mr.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 40284bbb45d6d..c4e496026c23a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4164,6 +4164,7 @@ static const struct uapi_definition mlx5_ib_defs[] = { static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev) { + mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); mlx5_ib_data_direct_cleanup(dev); mlx5_ib_cleanup_multiport_master(dev); WARN_ON(!xa_empty(&dev->odp_mkeys)); @@ -4229,6 +4230,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) if (err) goto err_mp; + mlx5_cmd_init_async_ctx(mdev, &dev->async_ctx); + return 0; err_mp: mlx5_ib_cleanup_multiport_master(dev); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 325fa04cbe8ae..56687add34f7d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -979,7 +979,6 @@ int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev) return -ENOMEM; } - mlx5_cmd_init_async_ctx(dev->mdev, &dev->async_ctx); timer_setup(&dev->delay_timer, delay_time_func, 0); mlx5_mkey_cache_debugfs_init(dev); mutex_lock(&cache->rb_lock); @@ -1041,7 +1040,6 @@ void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev) flush_workqueue(dev->cache.wq); mlx5_mkey_cache_debugfs_cleanup(dev); - mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); /* At this point all entries are disabled and have no concurrent work. */ mlx5r_destroy_cache_entries(dev); From 6ec6b746a179d07a0361bf712c5c08b2e3e6a415 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:37 +0200 Subject: [PATCH 186/214] IB/core: Introduce FRMR pools Add a generic Fast Registration Memory Region pools mechanism to allow drivers to optimize memory registration performance. Drivers that have the ability to reuse MRs or their underlying HW objects can take advantage of the mechanism to keep a 'handle' for those objects and use them upon user request. We assume that to achieve this goal a driver and its HW should implement a modify operation for the MRs that is able to at least clear and set the MRs and in more advanced implementations also support changing a subset of the MRs properties. The mechanism is built using an RB-tree consisting of pools, each pool represents a set of MR properties that are shared by all of the MRs residing in the pool and are unmodifiable by the vendor driver or HW. The exposed API from ib_core to the driver has 4 operations: Init and cleanup - handles data structs and locks for the pools. Push and pop - store and retrieve 'handle' for a memory registration or deregistrations request. The FRMR pools mechanism implements the logic to search the RB-tree for a pool with matching properties and create a new one when needed and requires the driver to implement creation and destruction of a 'handle' when pool is empty or a handle is requested or is being destroyed. Later patch will introduce Netlink API to interact with the FRMR pools mechanism to allow users to both configure and track its usage. A vendor wishing to configure FRMR pool without exposing it or without exposing internal MR properties to users, should use the kernel_vendor_key field in the pools key. This can be useful in a few cases, e.g, when the FRMR handle has a vendor-specific un-modifiable property that the user registering the memory might not be aware of. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/Makefile | 2 +- drivers/infiniband/core/frmr_pools.c | 328 +++++++++++++++++++++++++++ drivers/infiniband/core/frmr_pools.h | 48 ++++ include/rdma/frmr_pools.h | 37 +++ include/rdma/ib_verbs.h | 8 + 5 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 drivers/infiniband/core/frmr_pools.c create mode 100644 drivers/infiniband/core/frmr_pools.h create mode 100644 include/rdma/frmr_pools.h diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index f483e0c124445..7089a982b876f 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -12,7 +12,7 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \ roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \ multicast.o mad.o smi.o agent.o mad_rmpp.o \ nldev.o restrack.o counters.o ib_core_uverbs.o \ - trace.o lag.o + trace.o lag.o frmr_pools.o ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c new file mode 100644 index 0000000000000..073b2fcfb2cc7 --- /dev/null +++ b/drivers/infiniband/core/frmr_pools.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include +#include +#include +#include + +#include "frmr_pools.h" + +static int push_handle_to_queue_locked(struct frmr_queue *queue, u32 handle) +{ + u32 tmp = queue->ci % NUM_HANDLES_PER_PAGE; + struct frmr_handles_page *page; + + if (queue->ci >= queue->num_pages * NUM_HANDLES_PER_PAGE) { + page = kzalloc(sizeof(*page), GFP_ATOMIC); + if (!page) + return -ENOMEM; + queue->num_pages++; + list_add_tail(&page->list, &queue->pages_list); + } else { + page = list_last_entry(&queue->pages_list, + struct frmr_handles_page, list); + } + + page->handles[tmp] = handle; + queue->ci++; + return 0; +} + +static u32 pop_handle_from_queue_locked(struct frmr_queue *queue) +{ + u32 tmp = (queue->ci - 1) % NUM_HANDLES_PER_PAGE; + struct frmr_handles_page *page; + u32 handle; + + page = list_last_entry(&queue->pages_list, struct frmr_handles_page, + list); + handle = page->handles[tmp]; + queue->ci--; + + if (!tmp) { + list_del(&page->list); + queue->num_pages--; + kfree(page); + } + + return handle; +} + +static bool pop_frmr_handles_page(struct ib_frmr_pool *pool, + struct frmr_queue *queue, + struct frmr_handles_page **page, u32 *count) +{ + spin_lock(&pool->lock); + if (list_empty(&queue->pages_list)) { + spin_unlock(&pool->lock); + return false; + } + + *page = list_first_entry(&queue->pages_list, struct frmr_handles_page, + list); + list_del(&(*page)->list); + queue->num_pages--; + + /* If this is the last page, count may be less than + * NUM_HANDLES_PER_PAGE. + */ + if (queue->ci >= NUM_HANDLES_PER_PAGE) + *count = NUM_HANDLES_PER_PAGE; + else + *count = queue->ci; + + queue->ci -= *count; + spin_unlock(&pool->lock); + return true; +} + +static void destroy_frmr_pool(struct ib_device *device, + struct ib_frmr_pool *pool) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct frmr_handles_page *page; + u32 count; + + while (pop_frmr_handles_page(pool, &pool->queue, &page, &count)) { + pools->pool_ops->destroy_frmrs(device, page->handles, count); + kfree(page); + } + + rb_erase(&pool->node, &pools->rb_root); + kfree(pool); +} + +/* + * Initialize the FRMR pools for a device. + * + * @device: The device to initialize the FRMR pools for. + * @pool_ops: The pool operations to use. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pools_init(struct ib_device *device, + const struct ib_frmr_pool_ops *pool_ops) +{ + struct ib_frmr_pools *pools; + + pools = kzalloc(sizeof(*pools), GFP_KERNEL); + if (!pools) + return -ENOMEM; + + pools->rb_root = RB_ROOT; + rwlock_init(&pools->rb_lock); + pools->pool_ops = pool_ops; + + device->frmr_pools = pools; + return 0; +} +EXPORT_SYMBOL(ib_frmr_pools_init); + +/* + * Clean up the FRMR pools for a device. + * + * @device: The device to clean up the FRMR pools for. + * + * Call cleanup only after all FRMR handles have been pushed back to the pool + * and no other FRMR operations are allowed to run in parallel. + * Ensuring this allows us to save synchronization overhead in pop and push + * operations. + */ +void ib_frmr_pools_cleanup(struct ib_device *device) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct rb_node *node = rb_first(&pools->rb_root); + struct ib_frmr_pool *pool; + + while (node) { + struct rb_node *next = rb_next(node); + + pool = rb_entry(node, struct ib_frmr_pool, node); + destroy_frmr_pool(device, pool); + node = next; + } + + kfree(pools); + device->frmr_pools = NULL; +} +EXPORT_SYMBOL(ib_frmr_pools_cleanup); + +static int compare_keys(struct ib_frmr_key *key1, struct ib_frmr_key *key2) +{ + int res; + + res = key1->ats - key2->ats; + if (res) + return res; + + res = key1->access_flags - key2->access_flags; + if (res) + return res; + + res = key1->vendor_key - key2->vendor_key; + if (res) + return res; + + res = key1->kernel_vendor_key - key2->kernel_vendor_key; + if (res) + return res; + + /* + * allow using handles that support more DMA blocks, up to twice the + * requested number + */ + res = key1->num_dma_blocks - key2->num_dma_blocks; + if (res > 0 && res < key2->num_dma_blocks) + return 0; + + return res; +} + +static struct ib_frmr_pool *ib_frmr_pool_find(struct ib_frmr_pools *pools, + struct ib_frmr_key *key) +{ + struct rb_node *node = pools->rb_root.rb_node; + struct ib_frmr_pool *pool; + int cmp; + + /* find operation is done under read lock for performance reasons. + * The case of threads failing to find the same pool and creating it + * is handled by the create_frmr_pool function. + */ + read_lock(&pools->rb_lock); + while (node) { + pool = rb_entry(node, struct ib_frmr_pool, node); + cmp = compare_keys(&pool->key, key); + if (cmp < 0) { + node = node->rb_right; + } else if (cmp > 0) { + node = node->rb_left; + } else { + read_unlock(&pools->rb_lock); + return pool; + } + } + + read_unlock(&pools->rb_lock); + + return NULL; +} + +static struct ib_frmr_pool *create_frmr_pool(struct ib_device *device, + struct ib_frmr_key *key) +{ + struct rb_node **new = &device->frmr_pools->rb_root.rb_node, + *parent = NULL; + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + int cmp; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + memcpy(&pool->key, key, sizeof(*key)); + INIT_LIST_HEAD(&pool->queue.pages_list); + spin_lock_init(&pool->lock); + + write_lock(&pools->rb_lock); + while (*new) { + parent = *new; + cmp = compare_keys( + &rb_entry(parent, struct ib_frmr_pool, node)->key, key); + if (cmp < 0) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + /* If a different thread has already created the pool, return + * it. The insert operation is done under the write lock so we + * are sure that the pool is not inserted twice. + */ + if (cmp == 0) { + write_unlock(&pools->rb_lock); + kfree(pool); + return rb_entry(parent, struct ib_frmr_pool, node); + } + } + + rb_link_node(&pool->node, parent, new); + rb_insert_color(&pool->node, &pools->rb_root); + + write_unlock(&pools->rb_lock); + + return pool; +} + +static int get_frmr_from_pool(struct ib_device *device, + struct ib_frmr_pool *pool, struct ib_mr *mr) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + u32 handle; + int err; + + spin_lock(&pool->lock); + if (pool->queue.ci == 0) { + spin_unlock(&pool->lock); + err = pools->pool_ops->create_frmrs(device, &pool->key, &handle, + 1); + if (err) + return err; + } else { + handle = pop_handle_from_queue_locked(&pool->queue); + spin_unlock(&pool->lock); + } + + mr->frmr.pool = pool; + mr->frmr.handle = handle; + + return 0; +} + +/* + * Pop an FRMR handle from the pool. + * + * @device: The device to pop the FRMR handle from. + * @mr: The MR to pop the FRMR handle from. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + + WARN_ON_ONCE(!device->frmr_pools); + pool = ib_frmr_pool_find(pools, &mr->frmr.key); + if (!pool) { + pool = create_frmr_pool(device, &mr->frmr.key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + } + + return get_frmr_from_pool(device, pool, mr); +} +EXPORT_SYMBOL(ib_frmr_pool_pop); + +/* + * Push an FRMR handle back to the pool. + * + * @device: The device to push the FRMR handle to. + * @mr: The MR containing the FRMR handle to push back to the pool. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) +{ + struct ib_frmr_pool *pool = mr->frmr.pool; + int ret; + + spin_lock(&pool->lock); + ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); + spin_unlock(&pool->lock); + + return ret; +} +EXPORT_SYMBOL(ib_frmr_pool_push); diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h new file mode 100644 index 0000000000000..5a4d03b3d86f4 --- /dev/null +++ b/drivers/infiniband/core/frmr_pools.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef RDMA_CORE_FRMR_POOLS_H +#define RDMA_CORE_FRMR_POOLS_H + +#include +#include +#include +#include +#include + +#define NUM_HANDLES_PER_PAGE \ + ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(u32)) + +struct frmr_handles_page { + struct list_head list; + u32 handles[NUM_HANDLES_PER_PAGE]; +}; + +/* FRMR queue holds a list of frmr_handles_page. + * num_pages: number of pages in the queue. + * ci: current index in the handles array across all pages. + */ +struct frmr_queue { + struct list_head pages_list; + u32 num_pages; + unsigned long ci; +}; + +struct ib_frmr_pool { + struct rb_node node; + struct ib_frmr_key key; /* Pool key */ + + /* Protect access to the queue */ + spinlock_t lock; + struct frmr_queue queue; +}; + +struct ib_frmr_pools { + struct rb_root rb_root; + rwlock_t rb_lock; + const struct ib_frmr_pool_ops *pool_ops; +}; + +#endif /* RDMA_CORE_FRMR_POOLS_H */ diff --git a/include/rdma/frmr_pools.h b/include/rdma/frmr_pools.h new file mode 100644 index 0000000000000..da92ef4d7310c --- /dev/null +++ b/include/rdma/frmr_pools.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef FRMR_POOLS_H +#define FRMR_POOLS_H + +#include +#include + +struct ib_device; +struct ib_mr; + +struct ib_frmr_key { + u64 vendor_key; + /* A pool with non-zero kernel_vendor_key is a kernel-only pool. */ + u64 kernel_vendor_key; + size_t num_dma_blocks; + int access_flags; + u8 ats:1; +}; + +struct ib_frmr_pool_ops { + int (*create_frmrs)(struct ib_device *device, struct ib_frmr_key *key, + u32 *handles, u32 count); + void (*destroy_frmrs)(struct ib_device *device, u32 *handles, + u32 count); +}; + +int ib_frmr_pools_init(struct ib_device *device, + const struct ib_frmr_pool_ops *pool_ops); +void ib_frmr_pools_cleanup(struct ib_device *device); +int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr); +int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr); + +#endif /* FRMR_POOLS_H */ diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 6aad66bc5dd74..62c6f1b86f1f3 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -43,6 +43,7 @@ #include #include #include +#include #define IB_FW_VERSION_NAME_MAX ETHTOOL_FWVERS_LEN @@ -1887,6 +1888,11 @@ struct ib_mr { struct ib_dm *dm; struct ib_sig_attrs *sig_attrs; /* only for IB_MR_TYPE_INTEGRITY MRs */ struct ib_dmah *dmah; + struct { + struct ib_frmr_pool *pool; + struct ib_frmr_key key; + u32 handle; + } frmr; /* * Implementation details of the RDMA core, don't use in drivers: */ @@ -2880,6 +2886,8 @@ struct ib_device { struct list_head subdev_list; enum rdma_nl_name_assign_type name_assign_type; + + struct ib_frmr_pools *frmr_pools; }; static inline void *rdma_zalloc_obj(struct ib_device *dev, size_t size, From 978b76a9c0d170d4d0c6d9366854d796d6e24efe Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:38 +0200 Subject: [PATCH 187/214] RDMA/core: Add aging to FRMR pools Add aging mechanism to handles of FRMR pools. Keep the handles stored in FRMR pools for at least 1 minute for application to reuse, destroy all handles which were not reused. Add a new queue to each pool to accomplish that. Upon aging trigger, destroy all FRMR handles from the new 'inactive' queue and move all handles from the 'active' pool to the 'inactive' pool. This ensures all destroyed handles were not reused for at least one aging time period and were not held longer than 2 aging time periods. Handles from the inactive queue will be popped only if the active queue is empty. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/frmr_pools.c | 84 +++++++++++++++++++++++++--- drivers/infiniband/core/frmr_pools.h | 7 +++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 073b2fcfb2cc7..406664a6e2099 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -7,9 +7,12 @@ #include #include #include +#include #include "frmr_pools.h" +#define FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS 60 + static int push_handle_to_queue_locked(struct frmr_queue *queue, u32 handle) { u32 tmp = queue->ci % NUM_HANDLES_PER_PAGE; @@ -79,19 +82,58 @@ static bool pop_frmr_handles_page(struct ib_frmr_pool *pool, return true; } -static void destroy_frmr_pool(struct ib_device *device, - struct ib_frmr_pool *pool) +static void destroy_all_handles_in_queue(struct ib_device *device, + struct ib_frmr_pool *pool, + struct frmr_queue *queue) { struct ib_frmr_pools *pools = device->frmr_pools; struct frmr_handles_page *page; u32 count; - while (pop_frmr_handles_page(pool, &pool->queue, &page, &count)) { + while (pop_frmr_handles_page(pool, queue, &page, &count)) { pools->pool_ops->destroy_frmrs(device, page->handles, count); kfree(page); } +} + +static void pool_aging_work(struct work_struct *work) +{ + struct ib_frmr_pool *pool = container_of( + to_delayed_work(work), struct ib_frmr_pool, aging_work); + struct ib_frmr_pools *pools = pool->device->frmr_pools; + bool has_work = false; + + destroy_all_handles_in_queue(pool->device, pool, &pool->inactive_queue); + + /* Move all pages from regular queue to inactive queue */ + spin_lock(&pool->lock); + if (pool->queue.ci > 0) { + list_splice_tail_init(&pool->queue.pages_list, + &pool->inactive_queue.pages_list); + pool->inactive_queue.num_pages = pool->queue.num_pages; + pool->inactive_queue.ci = pool->queue.ci; + + pool->queue.num_pages = 0; + pool->queue.ci = 0; + has_work = true; + } + spin_unlock(&pool->lock); + + /* Reschedule if there are handles to age in next aging period */ + if (has_work) + queue_delayed_work( + pools->aging_wq, &pool->aging_work, + secs_to_jiffies(FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS)); +} + +static void destroy_frmr_pool(struct ib_device *device, + struct ib_frmr_pool *pool) +{ + cancel_delayed_work_sync(&pool->aging_work); + destroy_all_handles_in_queue(device, pool, &pool->queue); + destroy_all_handles_in_queue(device, pool, &pool->inactive_queue); - rb_erase(&pool->node, &pools->rb_root); + rb_erase(&pool->node, &device->frmr_pools->rb_root); kfree(pool); } @@ -115,6 +157,11 @@ int ib_frmr_pools_init(struct ib_device *device, pools->rb_root = RB_ROOT; rwlock_init(&pools->rb_lock); pools->pool_ops = pool_ops; + pools->aging_wq = create_singlethread_workqueue("frmr_aging_wq"); + if (!pools->aging_wq) { + kfree(pools); + return -ENOMEM; + } device->frmr_pools = pools; return 0; @@ -145,6 +192,7 @@ void ib_frmr_pools_cleanup(struct ib_device *device) node = next; } + destroy_workqueue(pools->aging_wq); kfree(pools); device->frmr_pools = NULL; } @@ -226,7 +274,10 @@ static struct ib_frmr_pool *create_frmr_pool(struct ib_device *device, memcpy(&pool->key, key, sizeof(*key)); INIT_LIST_HEAD(&pool->queue.pages_list); + INIT_LIST_HEAD(&pool->inactive_queue.pages_list); spin_lock_init(&pool->lock); + INIT_DELAYED_WORK(&pool->aging_work, pool_aging_work); + pool->device = device; write_lock(&pools->rb_lock); while (*new) { @@ -265,11 +316,17 @@ static int get_frmr_from_pool(struct ib_device *device, spin_lock(&pool->lock); if (pool->queue.ci == 0) { - spin_unlock(&pool->lock); - err = pools->pool_ops->create_frmrs(device, &pool->key, &handle, - 1); - if (err) - return err; + if (pool->inactive_queue.ci > 0) { + handle = pop_handle_from_queue_locked( + &pool->inactive_queue); + spin_unlock(&pool->lock); + } else { + spin_unlock(&pool->lock); + err = pools->pool_ops->create_frmrs(device, &pool->key, + &handle, 1); + if (err) + return err; + } } else { handle = pop_handle_from_queue_locked(&pool->queue); spin_unlock(&pool->lock); @@ -317,12 +374,21 @@ EXPORT_SYMBOL(ib_frmr_pool_pop); int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) { struct ib_frmr_pool *pool = mr->frmr.pool; + struct ib_frmr_pools *pools = device->frmr_pools; + bool schedule_aging = false; int ret; spin_lock(&pool->lock); + /* Schedule aging every time an empty pool becomes non-empty */ + if (pool->queue.ci == 0) + schedule_aging = true; ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); spin_unlock(&pool->lock); + if (ret == 0 && schedule_aging) + queue_delayed_work(pools->aging_wq, &pool->aging_work, + secs_to_jiffies(FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS)); + return ret; } EXPORT_SYMBOL(ib_frmr_pool_push); diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h index 5a4d03b3d86f4..a20323e03e3f4 100644 --- a/drivers/infiniband/core/frmr_pools.h +++ b/drivers/infiniband/core/frmr_pools.h @@ -11,6 +11,7 @@ #include #include #include +#include #define NUM_HANDLES_PER_PAGE \ ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(u32)) @@ -37,12 +38,18 @@ struct ib_frmr_pool { /* Protect access to the queue */ spinlock_t lock; struct frmr_queue queue; + struct frmr_queue inactive_queue; + + struct delayed_work aging_work; + struct ib_device *device; }; struct ib_frmr_pools { struct rb_root rb_root; rwlock_t rb_lock; const struct ib_frmr_pool_ops *pool_ops; + + struct workqueue_struct *aging_wq; }; #endif /* RDMA_CORE_FRMR_POOLS_H */ From ec26cb71e8e00667c7853b6af31dfec711fd9916 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:39 +0200 Subject: [PATCH 188/214] RDMA/core: Add FRMR pools statistics Count for each pool the number of FRMR handles popped and held by user MRs. Also keep track of the max value of this counter. Next patches will expose the statistics through netlink. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/frmr_pools.c | 12 ++++++++++-- drivers/infiniband/core/frmr_pools.h | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 406664a6e2099..9af2f6aa6c06c 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -319,19 +319,24 @@ static int get_frmr_from_pool(struct ib_device *device, if (pool->inactive_queue.ci > 0) { handle = pop_handle_from_queue_locked( &pool->inactive_queue); - spin_unlock(&pool->lock); } else { spin_unlock(&pool->lock); err = pools->pool_ops->create_frmrs(device, &pool->key, &handle, 1); if (err) return err; + spin_lock(&pool->lock); } } else { handle = pop_handle_from_queue_locked(&pool->queue); - spin_unlock(&pool->lock); } + pool->in_use++; + if (pool->in_use > pool->max_in_use) + pool->max_in_use = pool->in_use; + + spin_unlock(&pool->lock); + mr->frmr.pool = pool; mr->frmr.handle = handle; @@ -383,6 +388,9 @@ int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) if (pool->queue.ci == 0) schedule_aging = true; ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); + if (ret == 0) + pool->in_use--; + spin_unlock(&pool->lock); if (ret == 0 && schedule_aging) diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h index a20323e03e3f4..814d8a2106c29 100644 --- a/drivers/infiniband/core/frmr_pools.h +++ b/drivers/infiniband/core/frmr_pools.h @@ -42,6 +42,9 @@ struct ib_frmr_pool { struct delayed_work aging_work; struct ib_device *device; + + u32 max_in_use; + u32 in_use; }; struct ib_frmr_pools { From 60b9addb07255ccd0913ba3f1775d3e8a754f485 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:40 +0200 Subject: [PATCH 189/214] RDMA/core: Add pinned handles to FRMR pools Add a configuration of pinned handles on a specific FRMR pool. The configured amount of pinned handles will not be aged and will stay available for users to claim. Upon setting the amount of pinned handles to an FRMR pool, we will make sure we have at least the pinned amount of handles associated with the pool and create more, if necessary. The count for pinned handles take into account handles that are used by user MRs and handles in the queue. Introduce a new FRMR operation of build_key that allows drivers to manipulate FRMR keys supplied by the user, allowing failing for unsupported properties and masking of properties that are modifiable. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/frmr_pools.c | 128 +++++++++++++++++++++++++++ drivers/infiniband/core/frmr_pools.h | 3 + include/rdma/frmr_pools.h | 2 + 3 files changed, 133 insertions(+) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index 9af2f6aa6c06c..febe23920f562 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -96,6 +96,51 @@ static void destroy_all_handles_in_queue(struct ib_device *device, } } +static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + u32 total, to_destroy, destroyed = 0; + bool has_work = false; + u32 *handles; + u32 handle; + + spin_lock(&pool->lock); + total = pool->queue.ci + pool->inactive_queue.ci + pool->in_use; + if (total <= pool->pinned_handles) { + spin_unlock(&pool->lock); + return false; + } + + to_destroy = total - pool->pinned_handles; + + handles = kcalloc(to_destroy, sizeof(*handles), GFP_ATOMIC); + if (!handles) { + spin_unlock(&pool->lock); + return true; + } + + /* Destroy all excess handles in the inactive queue */ + while (pool->inactive_queue.ci && destroyed < to_destroy) { + handles[destroyed++] = pop_handle_from_queue_locked( + &pool->inactive_queue); + } + + /* Move all handles from regular queue to inactive queue */ + while (pool->queue.ci) { + handle = pop_handle_from_queue_locked(&pool->queue); + push_handle_to_queue_locked(&pool->inactive_queue, + handle); + has_work = true; + } + + spin_unlock(&pool->lock); + + if (destroyed) + pools->pool_ops->destroy_frmrs(device, handles, destroyed); + kfree(handles); + return has_work; +} + static void pool_aging_work(struct work_struct *work) { struct ib_frmr_pool *pool = container_of( @@ -103,6 +148,11 @@ static void pool_aging_work(struct work_struct *work) struct ib_frmr_pools *pools = pool->device->frmr_pools; bool has_work = false; + if (pool->pinned_handles) { + has_work = age_pinned_pool(pool->device, pool); + goto out; + } + destroy_all_handles_in_queue(pool->device, pool, &pool->inactive_queue); /* Move all pages from regular queue to inactive queue */ @@ -119,6 +169,7 @@ static void pool_aging_work(struct work_struct *work) } spin_unlock(&pool->lock); +out: /* Reschedule if there are handles to age in next aging period */ if (has_work) queue_delayed_work( @@ -307,6 +358,83 @@ static struct ib_frmr_pool *create_frmr_pool(struct ib_device *device, return pool; } +int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, + u32 pinned_handles) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_key driver_key = {}; + struct ib_frmr_pool *pool; + u32 needed_handles; + u32 current_total; + int i, ret = 0; + u32 *handles; + + if (!pools) + return -EINVAL; + + ret = ib_check_mr_access(device, key->access_flags); + if (ret) + return ret; + + if (pools->pool_ops->build_key) { + ret = pools->pool_ops->build_key(device, key, &driver_key); + if (ret) + return ret; + } else { + memcpy(&driver_key, key, sizeof(*key)); + } + + pool = ib_frmr_pool_find(pools, &driver_key); + if (!pool) { + pool = create_frmr_pool(device, &driver_key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + } + + spin_lock(&pool->lock); + current_total = pool->in_use + pool->queue.ci + pool->inactive_queue.ci; + + if (current_total < pinned_handles) + needed_handles = pinned_handles - current_total; + else + needed_handles = 0; + + pool->pinned_handles = pinned_handles; + spin_unlock(&pool->lock); + + if (!needed_handles) + goto schedule_aging; + + handles = kcalloc(needed_handles, sizeof(*handles), GFP_KERNEL); + if (!handles) + return -ENOMEM; + + ret = pools->pool_ops->create_frmrs(device, key, handles, + needed_handles); + if (ret) { + kfree(handles); + return ret; + } + + spin_lock(&pool->lock); + for (i = 0; i < needed_handles; i++) { + ret = push_handle_to_queue_locked(&pool->queue, + handles[i]); + if (ret) + goto end; + } + +end: + spin_unlock(&pool->lock); + kfree(handles); + +schedule_aging: + /* Ensure aging is scheduled to adjust to new pinned handles count */ + mod_delayed_work(pools->aging_wq, &pool->aging_work, 0); + + return ret; +} + static int get_frmr_from_pool(struct ib_device *device, struct ib_frmr_pool *pool, struct ib_mr *mr) { diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h index 814d8a2106c29..b144273ee3478 100644 --- a/drivers/infiniband/core/frmr_pools.h +++ b/drivers/infiniband/core/frmr_pools.h @@ -45,6 +45,7 @@ struct ib_frmr_pool { u32 max_in_use; u32 in_use; + u32 pinned_handles; }; struct ib_frmr_pools { @@ -55,4 +56,6 @@ struct ib_frmr_pools { struct workqueue_struct *aging_wq; }; +int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, + u32 pinned_handles); #endif /* RDMA_CORE_FRMR_POOLS_H */ diff --git a/include/rdma/frmr_pools.h b/include/rdma/frmr_pools.h index da92ef4d7310c..333ce31fc762e 100644 --- a/include/rdma/frmr_pools.h +++ b/include/rdma/frmr_pools.h @@ -26,6 +26,8 @@ struct ib_frmr_pool_ops { u32 *handles, u32 count); void (*destroy_frmrs)(struct ib_device *device, u32 *handles, u32 count); + int (*build_key)(struct ib_device *device, const struct ib_frmr_key *in, + struct ib_frmr_key *out); }; int ib_frmr_pools_init(struct ib_device *device, From fa5fe31e72150b27c8cba01d310b7d571d28805c Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:41 +0200 Subject: [PATCH 190/214] RDMA/mlx5: Switch from MR cache to FRMR pools Use the new generic FRMR pools mechanism to optimize the performance of memory registrations. The move to the new generic FRMR pools will allow users configuring MR cache through debugfs of MR cache to use the netlink API for FRMR pools which will be added later in this series. Thus being able to have more flexibility configuring the kernel and also being able to configure on machines where debugfs is not available. Mlx5_ib will save the mkey index as the handle in FRMR pools, same as the MR cache implementation. Upon each memory registration mlx5_ib will try to pull a handle from FRMR pools and upon each deregistration it will push the handle back to it's appropriate pool. Use the vendor key field in umr pool key to save the access mode of the mkey. Use the option for kernel-only FRMR pool to manage the mkeys used for registration with DMAH as the translation between UAPI of DMAH and the mkey property of st_index is non-trivial and changing dynamically. Since the value for no PH is 0xff and not zero, switch between them in the frmr_key to have a zero'ed kernel_vendor_key when not using DMAH. Remove the limitation we had with MR cache for mkeys up to 2^20 dma blocks and support mkeys up to HW limitations according to caps. Remove all MR cache related code. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/hw/mlx5/main.c | 7 +- drivers/infiniband/hw/mlx5/mlx5_ib.h | 86 +- drivers/infiniband/hw/mlx5/mr.c | 1143 +++++--------------------- drivers/infiniband/hw/mlx5/odp.c | 19 - drivers/infiniband/hw/mlx5/umr.h | 1 + 5 files changed, 191 insertions(+), 1065 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index c4e496026c23a..7b0d6e8ce8d7d 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4609,7 +4609,7 @@ static int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev) static void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev) { - mlx5_mkey_cache_cleanup(dev); + mlx5r_frmr_pools_cleanup(&dev->ib_dev); mlx5r_umr_resource_cleanup(dev); mlx5r_umr_cleanup(dev); } @@ -4627,9 +4627,10 @@ static int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev) if (ret) return ret; - ret = mlx5_mkey_cache_init(dev); + ret = mlx5r_frmr_pools_init(&dev->ib_dev); if (ret) - mlx5_ib_warn(dev, "mr cache init failed %d\n", ret); + mlx5_ib_warn(dev, "frmr pools init failed %d\n", ret); + return ret; } diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 09d82d5f95e35..da7bd4d6df3c2 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -641,25 +641,12 @@ enum mlx5_mkey_type { /* Used for non-existent ph value */ #define MLX5_IB_NO_PH 0xff -struct mlx5r_cache_rb_key { - u8 ats:1; - u8 ph; - u16 st_index; - unsigned int access_mode; - unsigned int access_flags; - unsigned int ndescs; -}; - struct mlx5_ib_mkey { u32 key; enum mlx5_mkey_type type; unsigned int ndescs; struct wait_queue_head wait; refcount_t usecount; - /* Cacheable user Mkey must hold either a rb_key or a cache_ent. */ - struct mlx5r_cache_rb_key rb_key; - struct mlx5_cache_ent *cache_ent; - u8 cacheable : 1; }; #define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE) @@ -784,68 +771,6 @@ struct umr_common { struct mutex init_lock; }; -#define NUM_MKEYS_PER_PAGE \ - ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(u32)) - -struct mlx5_mkeys_page { - u32 mkeys[NUM_MKEYS_PER_PAGE]; - struct list_head list; -}; -static_assert(sizeof(struct mlx5_mkeys_page) == PAGE_SIZE); - -struct mlx5_mkeys_queue { - struct list_head pages_list; - u32 num_pages; - unsigned long ci; - spinlock_t lock; /* sync list ops */ -}; - -struct mlx5_cache_ent { - struct mlx5_mkeys_queue mkeys_queue; - u32 pending; - - char name[4]; - - struct rb_node node; - struct mlx5r_cache_rb_key rb_key; - - u8 is_tmp:1; - u8 disabled:1; - u8 fill_to_high_water:1; - u8 tmp_cleanup_scheduled:1; - - /* - * - limit is the low water mark for stored mkeys, 2* limit is the - * upper water mark. - */ - u32 in_use; - u32 limit; - - /* Statistics */ - u32 miss; - - struct mlx5_ib_dev *dev; - struct delayed_work dwork; -}; - -struct mlx5r_async_create_mkey { - union { - u32 in[MLX5_ST_SZ_BYTES(create_mkey_in)]; - u32 out[MLX5_ST_SZ_DW(create_mkey_out)]; - }; - struct mlx5_async_work cb_work; - struct mlx5_cache_ent *ent; - u32 mkey; -}; - -struct mlx5_mkey_cache { - struct workqueue_struct *wq; - struct rb_root rb_root; - struct mutex rb_lock; - struct dentry *fs_root; - unsigned long last_add; -}; - struct mlx5_ib_port_resources { struct mlx5_ib_gsi_qp *gsi; struct work_struct pkey_change_work; @@ -1180,8 +1105,6 @@ struct mlx5_ib_dev { struct mlx5_ib_resources devr; atomic_t mkey_var; - struct mlx5_mkey_cache cache; - struct timer_list delay_timer; /* Prevents soft lock on massive reg MRs */ struct mutex slow_path_mutex; struct ib_odp_caps odp_caps; @@ -1438,13 +1361,8 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u32 port, void mlx5_ib_populate_pas(struct ib_umem *umem, size_t page_size, __be64 *pas, u64 access_flags); int mlx5_ib_get_cqe_size(struct ib_cq *ibcq); -int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev); -void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev); -struct mlx5_cache_ent * -mlx5r_cache_create_ent_locked(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key, - bool persistent_entry); - +int mlx5r_frmr_pools_init(struct ib_device *device); +void mlx5r_frmr_pools_cleanup(struct ib_device *device); struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int access_flags, int access_mode, int ndescs); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 56687add34f7d..eec1a750ee9fa 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -31,7 +31,6 @@ * SOFTWARE. */ - #include #include #include @@ -39,6 +38,7 @@ #include #include #include +#include #include #include "dm.h" #include "mlx5_ib.h" @@ -46,15 +46,15 @@ #include "data_direct.h" #include "dmah.h" -enum { - MAX_PENDING_REG_MR = 8, -}; - -#define MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS 4 #define MLX5_UMR_ALIGN 2048 -static void -create_mkey_callback(int status, struct mlx5_async_work *context); +static int mkey_max_umr_order(struct mlx5_ib_dev *dev) +{ + if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) + return MLX5_MAX_UMR_EXTENDED_SHIFT; + return MLX5_MAX_UMR_SHIFT; +} + static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, struct ib_umem *umem, u64 iova, int access_flags, unsigned long page_size, bool populate, @@ -111,23 +111,6 @@ static int mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, return ret; } -static int mlx5_ib_create_mkey_cb(struct mlx5r_async_create_mkey *async_create) -{ - struct mlx5_ib_dev *dev = async_create->ent->dev; - size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - size_t outlen = MLX5_ST_SZ_BYTES(create_mkey_out); - - MLX5_SET(create_mkey_in, async_create->in, opcode, - MLX5_CMD_OP_CREATE_MKEY); - assign_mkey_variant(dev, &async_create->mkey, async_create->in); - return mlx5_cmd_exec_cb(&dev->async_ctx, async_create->in, inlen, - async_create->out, outlen, create_mkey_callback, - &async_create->cb_work); -} - -static int mkey_cache_max_order(struct mlx5_ib_dev *dev); -static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent); - static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) { WARN_ON(xa_load(&dev->odp_mkeys, mlx5_base_mkey(mr->mmkey.key))); @@ -135,94 +118,6 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) return mlx5_core_destroy_mkey(dev->mdev, mr->mmkey.key); } -static void create_mkey_warn(struct mlx5_ib_dev *dev, int status, void *out) -{ - if (status == -ENXIO) /* core driver is not available */ - return; - - mlx5_ib_warn(dev, "async reg mr failed. status %d\n", status); - if (status != -EREMOTEIO) /* driver specific failure */ - return; - - /* Failed in FW, print cmd out failure details */ - mlx5_cmd_out_err(dev->mdev, MLX5_CMD_OP_CREATE_MKEY, 0, out); -} - -static int push_mkey_locked(struct mlx5_cache_ent *ent, u32 mkey) -{ - unsigned long tmp = ent->mkeys_queue.ci % NUM_MKEYS_PER_PAGE; - struct mlx5_mkeys_page *page; - - lockdep_assert_held(&ent->mkeys_queue.lock); - if (ent->mkeys_queue.ci >= - ent->mkeys_queue.num_pages * NUM_MKEYS_PER_PAGE) { - page = kzalloc(sizeof(*page), GFP_ATOMIC); - if (!page) - return -ENOMEM; - ent->mkeys_queue.num_pages++; - list_add_tail(&page->list, &ent->mkeys_queue.pages_list); - } else { - page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - } - - page->mkeys[tmp] = mkey; - ent->mkeys_queue.ci++; - return 0; -} - -static int pop_mkey_locked(struct mlx5_cache_ent *ent) -{ - unsigned long tmp = (ent->mkeys_queue.ci - 1) % NUM_MKEYS_PER_PAGE; - struct mlx5_mkeys_page *last_page; - u32 mkey; - - lockdep_assert_held(&ent->mkeys_queue.lock); - last_page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - mkey = last_page->mkeys[tmp]; - last_page->mkeys[tmp] = 0; - ent->mkeys_queue.ci--; - if (ent->mkeys_queue.num_pages > 1 && !tmp) { - list_del(&last_page->list); - ent->mkeys_queue.num_pages--; - kfree(last_page); - } - return mkey; -} - -static void create_mkey_callback(int status, struct mlx5_async_work *context) -{ - struct mlx5r_async_create_mkey *mkey_out = - container_of(context, struct mlx5r_async_create_mkey, cb_work); - struct mlx5_cache_ent *ent = mkey_out->ent; - struct mlx5_ib_dev *dev = ent->dev; - unsigned long flags; - - if (status) { - create_mkey_warn(dev, status, mkey_out->out); - kfree(mkey_out); - spin_lock_irqsave(&ent->mkeys_queue.lock, flags); - ent->pending--; - WRITE_ONCE(dev->fill_delay, 1); - spin_unlock_irqrestore(&ent->mkeys_queue.lock, flags); - mod_timer(&dev->delay_timer, jiffies + HZ); - return; - } - - mkey_out->mkey |= mlx5_idx_to_mkey( - MLX5_GET(create_mkey_out, mkey_out->out, mkey_index)); - WRITE_ONCE(dev->cache.last_add, jiffies); - - spin_lock_irqsave(&ent->mkeys_queue.lock, flags); - push_mkey_locked(ent, mkey_out->mkey); - ent->pending--; - /* If we are doing fill_to_high_water then keep going. */ - queue_adjust_cache_locked(ent); - spin_unlock_irqrestore(&ent->mkeys_queue.lock, flags); - kfree(mkey_out); -} - static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) { int ret = 0; @@ -242,538 +137,6 @@ static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) return ret; } -static void set_cache_mkc(struct mlx5_cache_ent *ent, void *mkc) -{ - set_mkc_access_pd_addr_fields(mkc, ent->rb_key.access_flags, 0, - ent->dev->umrc.pd); - MLX5_SET(mkc, mkc, free, 1); - MLX5_SET(mkc, mkc, umr_en, 1); - MLX5_SET(mkc, mkc, access_mode_1_0, ent->rb_key.access_mode & 0x3); - MLX5_SET(mkc, mkc, access_mode_4_2, - (ent->rb_key.access_mode >> 2) & 0x7); - MLX5_SET(mkc, mkc, ma_translation_mode, !!ent->rb_key.ats); - - MLX5_SET(mkc, mkc, translations_octword_size, - get_mkc_octo_size(ent->rb_key.access_mode, - ent->rb_key.ndescs)); - MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); - - if (ent->rb_key.ph != MLX5_IB_NO_PH) { - MLX5_SET(mkc, mkc, pcie_tph_en, 1); - MLX5_SET(mkc, mkc, pcie_tph_ph, ent->rb_key.ph); - if (ent->rb_key.st_index != MLX5_MKC_PCIE_TPH_NO_STEERING_TAG_INDEX) - MLX5_SET(mkc, mkc, pcie_tph_steering_tag_index, - ent->rb_key.st_index); - } -} - -/* Asynchronously schedule new MRs to be populated in the cache. */ -static int add_keys(struct mlx5_cache_ent *ent, unsigned int num) -{ - struct mlx5r_async_create_mkey *async_create; - void *mkc; - int err = 0; - int i; - - for (i = 0; i < num; i++) { - async_create = kzalloc(sizeof(struct mlx5r_async_create_mkey), - GFP_KERNEL); - if (!async_create) - return -ENOMEM; - mkc = MLX5_ADDR_OF(create_mkey_in, async_create->in, - memory_key_mkey_entry); - set_cache_mkc(ent, mkc); - async_create->ent = ent; - - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->pending >= MAX_PENDING_REG_MR) { - err = -EAGAIN; - goto free_async_create; - } - ent->pending++; - spin_unlock_irq(&ent->mkeys_queue.lock); - - err = mlx5_ib_create_mkey_cb(async_create); - if (err) { - mlx5_ib_warn(ent->dev, "create mkey failed %d\n", err); - goto err_create_mkey; - } - } - - return 0; - -err_create_mkey: - spin_lock_irq(&ent->mkeys_queue.lock); - ent->pending--; -free_async_create: - spin_unlock_irq(&ent->mkeys_queue.lock); - kfree(async_create); - return err; -} - -/* Synchronously create a MR in the cache */ -static int create_cache_mkey(struct mlx5_cache_ent *ent, u32 *mkey) -{ - size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - void *mkc; - u32 *in; - int err; - - in = kzalloc(inlen, GFP_KERNEL); - if (!in) - return -ENOMEM; - mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - set_cache_mkc(ent, mkc); - - err = mlx5_core_create_mkey(ent->dev->mdev, mkey, in, inlen); - if (err) - goto free_in; - - WRITE_ONCE(ent->dev->cache.last_add, jiffies); -free_in: - kfree(in); - return err; -} - -static void remove_cache_mr_locked(struct mlx5_cache_ent *ent) -{ - u32 mkey; - - lockdep_assert_held(&ent->mkeys_queue.lock); - if (!ent->mkeys_queue.ci) - return; - mkey = pop_mkey_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - mlx5_core_destroy_mkey(ent->dev->mdev, mkey); - spin_lock_irq(&ent->mkeys_queue.lock); -} - -static int resize_available_mrs(struct mlx5_cache_ent *ent, unsigned int target, - bool limit_fill) - __acquires(&ent->mkeys_queue.lock) __releases(&ent->mkeys_queue.lock) -{ - int err; - - lockdep_assert_held(&ent->mkeys_queue.lock); - - while (true) { - if (limit_fill) - target = ent->limit * 2; - if (target == ent->pending + ent->mkeys_queue.ci) - return 0; - if (target > ent->pending + ent->mkeys_queue.ci) { - u32 todo = target - (ent->pending + ent->mkeys_queue.ci); - - spin_unlock_irq(&ent->mkeys_queue.lock); - err = add_keys(ent, todo); - if (err == -EAGAIN) - usleep_range(3000, 5000); - spin_lock_irq(&ent->mkeys_queue.lock); - if (err) { - if (err != -EAGAIN) - return err; - } else - return 0; - } else { - remove_cache_mr_locked(ent); - } - } -} - -static ssize_t size_write(struct file *filp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - u32 target; - int err; - - err = kstrtou32_from_user(buf, count, 0, &target); - if (err) - return err; - - /* - * Target is the new value of total_mrs the user requests, however we - * cannot free MRs that are in use. Compute the target value for stored - * mkeys. - */ - spin_lock_irq(&ent->mkeys_queue.lock); - if (target < ent->in_use) { - err = -EINVAL; - goto err_unlock; - } - target = target - ent->in_use; - if (target < ent->limit || target > ent->limit*2) { - err = -EINVAL; - goto err_unlock; - } - err = resize_available_mrs(ent, target, false); - if (err) - goto err_unlock; - spin_unlock_irq(&ent->mkeys_queue.lock); - - return count; - -err_unlock: - spin_unlock_irq(&ent->mkeys_queue.lock); - return err; -} - -static ssize_t size_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - char lbuf[20]; - int err; - - err = snprintf(lbuf, sizeof(lbuf), "%ld\n", - ent->mkeys_queue.ci + ent->in_use); - if (err < 0) - return err; - - return simple_read_from_buffer(buf, count, pos, lbuf, err); -} - -static const struct file_operations size_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = size_write, - .read = size_read, -}; - -static ssize_t limit_write(struct file *filp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - u32 var; - int err; - - err = kstrtou32_from_user(buf, count, 0, &var); - if (err) - return err; - - /* - * Upon set we immediately fill the cache to high water mark implied by - * the limit. - */ - spin_lock_irq(&ent->mkeys_queue.lock); - ent->limit = var; - err = resize_available_mrs(ent, 0, true); - spin_unlock_irq(&ent->mkeys_queue.lock); - if (err) - return err; - return count; -} - -static ssize_t limit_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - char lbuf[20]; - int err; - - err = snprintf(lbuf, sizeof(lbuf), "%d\n", ent->limit); - if (err < 0) - return err; - - return simple_read_from_buffer(buf, count, pos, lbuf, err); -} - -static const struct file_operations limit_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = limit_write, - .read = limit_read, -}; - -static bool someone_adding(struct mlx5_mkey_cache *cache) -{ - struct mlx5_cache_ent *ent; - struct rb_node *node; - bool ret; - - mutex_lock(&cache->rb_lock); - for (node = rb_first(&cache->rb_root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - ret = ent->mkeys_queue.ci < ent->limit; - spin_unlock_irq(&ent->mkeys_queue.lock); - if (ret) { - mutex_unlock(&cache->rb_lock); - return true; - } - } - mutex_unlock(&cache->rb_lock); - return false; -} - -/* - * Check if the bucket is outside the high/low water mark and schedule an async - * update. The cache refill has hysteresis, once the low water mark is hit it is - * refilled up to the high mark. - */ -static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent) -{ - lockdep_assert_held(&ent->mkeys_queue.lock); - - if (ent->disabled || READ_ONCE(ent->dev->fill_delay) || ent->is_tmp) - return; - if (ent->mkeys_queue.ci < ent->limit) { - ent->fill_to_high_water = true; - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } else if (ent->fill_to_high_water && - ent->mkeys_queue.ci + ent->pending < 2 * ent->limit) { - /* - * Once we start populating due to hitting a low water mark - * continue until we pass the high water mark. - */ - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } else if (ent->mkeys_queue.ci == 2 * ent->limit) { - ent->fill_to_high_water = false; - } else if (ent->mkeys_queue.ci > 2 * ent->limit) { - /* Queue deletion of excess entries */ - ent->fill_to_high_water = false; - if (ent->pending) - queue_delayed_work(ent->dev->cache.wq, &ent->dwork, - secs_to_jiffies(1)); - else - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } -} - -static void clean_keys(struct mlx5_ib_dev *dev, struct mlx5_cache_ent *ent) -{ - u32 mkey; - - spin_lock_irq(&ent->mkeys_queue.lock); - while (ent->mkeys_queue.ci) { - mkey = pop_mkey_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - mlx5_core_destroy_mkey(dev->mdev, mkey); - spin_lock_irq(&ent->mkeys_queue.lock); - } - ent->tmp_cleanup_scheduled = false; - spin_unlock_irq(&ent->mkeys_queue.lock); -} - -static void __cache_work_func(struct mlx5_cache_ent *ent) -{ - struct mlx5_ib_dev *dev = ent->dev; - struct mlx5_mkey_cache *cache = &dev->cache; - int err; - - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - - if (ent->fill_to_high_water && - ent->mkeys_queue.ci + ent->pending < 2 * ent->limit && - !READ_ONCE(dev->fill_delay)) { - spin_unlock_irq(&ent->mkeys_queue.lock); - err = add_keys(ent, 1); - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - if (err) { - /* - * EAGAIN only happens if there are pending MRs, so we - * will be rescheduled when storing them. The only - * failure path here is ENOMEM. - */ - if (err != -EAGAIN) { - mlx5_ib_warn( - dev, - "add keys command failed, err %d\n", - err); - queue_delayed_work(cache->wq, &ent->dwork, - secs_to_jiffies(1)); - } - } - } else if (ent->mkeys_queue.ci > 2 * ent->limit) { - bool need_delay; - - /* - * The remove_cache_mr() logic is performed as garbage - * collection task. Such task is intended to be run when no - * other active processes are running. - * - * The need_resched() will return TRUE if there are user tasks - * to be activated in near future. - * - * In such case, we don't execute remove_cache_mr() and postpone - * the garbage collection work to try to run in next cycle, in - * order to free CPU resources to other tasks. - */ - spin_unlock_irq(&ent->mkeys_queue.lock); - need_delay = need_resched() || someone_adding(cache) || - !time_after(jiffies, - READ_ONCE(cache->last_add) + 300 * HZ); - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - if (need_delay) { - queue_delayed_work(cache->wq, &ent->dwork, 300 * HZ); - goto out; - } - remove_cache_mr_locked(ent); - queue_adjust_cache_locked(ent); - } -out: - spin_unlock_irq(&ent->mkeys_queue.lock); -} - -static void delayed_cache_work_func(struct work_struct *work) -{ - struct mlx5_cache_ent *ent; - - ent = container_of(work, struct mlx5_cache_ent, dwork.work); - /* temp entries are never filled, only cleaned */ - if (ent->is_tmp) - clean_keys(ent->dev, ent); - else - __cache_work_func(ent); -} - -static int cache_ent_key_cmp(struct mlx5r_cache_rb_key key1, - struct mlx5r_cache_rb_key key2) -{ - int res; - - res = key1.ats - key2.ats; - if (res) - return res; - - res = key1.access_mode - key2.access_mode; - if (res) - return res; - - res = key1.access_flags - key2.access_flags; - if (res) - return res; - - res = key1.st_index - key2.st_index; - if (res) - return res; - - res = key1.ph - key2.ph; - if (res) - return res; - - /* - * keep ndescs the last in the compare table since the find function - * searches for an exact match on all properties and only closest - * match in size. - */ - return key1.ndescs - key2.ndescs; -} - -static int mlx5_cache_ent_insert(struct mlx5_mkey_cache *cache, - struct mlx5_cache_ent *ent) -{ - struct rb_node **new = &cache->rb_root.rb_node, *parent = NULL; - struct mlx5_cache_ent *cur; - int cmp; - - /* Figure out where to put new node */ - while (*new) { - cur = rb_entry(*new, struct mlx5_cache_ent, node); - parent = *new; - cmp = cache_ent_key_cmp(cur->rb_key, ent->rb_key); - if (cmp > 0) - new = &((*new)->rb_left); - if (cmp < 0) - new = &((*new)->rb_right); - if (cmp == 0) - return -EEXIST; - } - - /* Add new node and rebalance tree. */ - rb_link_node(&ent->node, parent, new); - rb_insert_color(&ent->node, &cache->rb_root); - - return 0; -} - -static struct mlx5_cache_ent * -mkey_cache_ent_from_rb_key(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key) -{ - struct rb_node *node = dev->cache.rb_root.rb_node; - struct mlx5_cache_ent *cur, *smallest = NULL; - u64 ndescs_limit; - int cmp; - - /* - * Find the smallest ent with order >= requested_order. - */ - while (node) { - cur = rb_entry(node, struct mlx5_cache_ent, node); - cmp = cache_ent_key_cmp(cur->rb_key, rb_key); - if (cmp > 0) { - smallest = cur; - node = node->rb_left; - } - if (cmp < 0) - node = node->rb_right; - if (cmp == 0) - return cur; - } - - /* - * Limit the usage of mkeys larger than twice the required size while - * also allowing the usage of smallest cache entry for small MRs. - */ - ndescs_limit = max_t(u64, rb_key.ndescs * 2, - MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS); - - return (smallest && - smallest->rb_key.access_mode == rb_key.access_mode && - smallest->rb_key.access_flags == rb_key.access_flags && - smallest->rb_key.ats == rb_key.ats && - smallest->rb_key.st_index == rb_key.st_index && - smallest->rb_key.ph == rb_key.ph && - smallest->rb_key.ndescs <= ndescs_limit) ? - smallest : - NULL; -} - -static struct mlx5_ib_mr *_mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, - struct mlx5_cache_ent *ent) -{ - struct mlx5_ib_mr *mr; - int err; - - mr = kzalloc(sizeof(*mr), GFP_KERNEL); - if (!mr) - return ERR_PTR(-ENOMEM); - - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use++; - - if (!ent->mkeys_queue.ci) { - queue_adjust_cache_locked(ent); - ent->miss++; - spin_unlock_irq(&ent->mkeys_queue.lock); - err = create_cache_mkey(ent, &mr->mmkey.key); - if (err) { - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use--; - spin_unlock_irq(&ent->mkeys_queue.lock); - kfree(mr); - return ERR_PTR(err); - } - } else { - mr->mmkey.key = pop_mkey_locked(ent); - queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - } - mr->mmkey.cache_ent = ent; - mr->mmkey.type = MLX5_MKEY_MR; - mr->mmkey.rb_key = ent->rb_key; - mr->mmkey.cacheable = true; - init_waitqueue_head(&mr->mmkey.wait); - return mr; -} - static int get_unchangeable_access_flags(struct mlx5_ib_dev *dev, int access_flags) { @@ -798,254 +161,201 @@ static int get_unchangeable_access_flags(struct mlx5_ib_dev *dev, return ret; } -struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, - int access_flags, int access_mode, - int ndescs) -{ - struct mlx5r_cache_rb_key rb_key = { - .ndescs = ndescs, - .access_mode = access_mode, - .access_flags = get_unchangeable_access_flags(dev, access_flags), - .ph = MLX5_IB_NO_PH, - }; - struct mlx5_cache_ent *ent = mkey_cache_ent_from_rb_key(dev, rb_key); +#define MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK 1ULL +#define MLX5_FRMR_POOLS_KEY_VENDOR_KEY_SUPPORTED \ + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK - if (!ent) - return ERR_PTR(-EOPNOTSUPP); - - return _mlx5_mr_cache_alloc(dev, ent); -} - -static void mlx5_mkey_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) -{ - if (!mlx5_debugfs_root || dev->is_rep) - return; +#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT 16 +#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK 0xFF0000 +#define MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK 0xFFFF - debugfs_remove_recursive(dev->cache.fs_root); - dev->cache.fs_root = NULL; -} - -static void mlx5_mkey_cache_debugfs_add_ent(struct mlx5_ib_dev *dev, - struct mlx5_cache_ent *ent) +static struct mlx5_ib_mr * +_mlx5_frmr_pool_alloc(struct mlx5_ib_dev *dev, struct ib_umem *umem, + int access_flags, int access_mode, + unsigned long page_size, u16 st_index, u8 ph) { - int order = order_base_2(ent->rb_key.ndescs); - struct dentry *dir; - - if (!mlx5_debugfs_root || dev->is_rep) - return; - - if (ent->rb_key.access_mode == MLX5_MKC_ACCESS_MODE_KSM) - order = MLX5_IMR_KSM_CACHE_ENTRY + 2; - - sprintf(ent->name, "%d", order); - dir = debugfs_create_dir(ent->name, dev->cache.fs_root); - debugfs_create_file("size", 0600, dir, ent, &size_fops); - debugfs_create_file("limit", 0600, dir, ent, &limit_fops); - debugfs_create_ulong("cur", 0400, dir, &ent->mkeys_queue.ci); - debugfs_create_u32("miss", 0600, dir, &ent->miss); -} + struct mlx5_ib_mr *mr; + int err; -static void mlx5_mkey_cache_debugfs_init(struct mlx5_ib_dev *dev) -{ - struct dentry *dbg_root = mlx5_debugfs_get_dev_root(dev->mdev); - struct mlx5_mkey_cache *cache = &dev->cache; + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); - if (!mlx5_debugfs_root || dev->is_rep) - return; + mr->ibmr.frmr.key.ats = mlx5_umem_needs_ats(dev, umem, access_flags); + mr->ibmr.frmr.key.access_flags = + get_unchangeable_access_flags(dev, access_flags); + mr->ibmr.frmr.key.num_dma_blocks = + ib_umem_num_dma_blocks(umem, page_size); + mr->ibmr.frmr.key.vendor_key = + access_mode == MLX5_MKC_ACCESS_MODE_KSM ? + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK : + 0; + + /* Normalize ph: swap 0 and MLX5_IB_NO_PH */ + if (ph == MLX5_IB_NO_PH || ph == 0) + ph ^= MLX5_IB_NO_PH; + + mr->ibmr.frmr.key.kernel_vendor_key = + st_index | (ph << MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT); + err = ib_frmr_pool_pop(&dev->ib_dev, &mr->ibmr); + if (err) { + kfree(mr); + return ERR_PTR(err); + } + mr->mmkey.key = mr->ibmr.frmr.handle; + init_waitqueue_head(&mr->mmkey.wait); - cache->fs_root = debugfs_create_dir("mr_cache", dbg_root); + return mr; } -static void delay_time_func(struct timer_list *t) +struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, + int access_flags, int access_mode, + int ndescs) { - struct mlx5_ib_dev *dev = timer_container_of(dev, t, delay_timer); - - WRITE_ONCE(dev->fill_delay, 0); -} + struct ib_frmr_key key = { + .access_flags = + get_unchangeable_access_flags(dev, access_flags), + .vendor_key = access_mode == MLX5_MKC_ACCESS_MODE_MTT ? + 0 : + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK, + .num_dma_blocks = ndescs, + .kernel_vendor_key = 0, /* no PH and no ST index */ + }; + struct mlx5_ib_mr *mr; + int ret; -static int mlx5r_mkeys_init(struct mlx5_cache_ent *ent) -{ - struct mlx5_mkeys_page *page; + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); - page = kzalloc(sizeof(*page), GFP_KERNEL); - if (!page) - return -ENOMEM; - INIT_LIST_HEAD(&ent->mkeys_queue.pages_list); - spin_lock_init(&ent->mkeys_queue.lock); - list_add_tail(&page->list, &ent->mkeys_queue.pages_list); - ent->mkeys_queue.num_pages++; - return 0; -} + init_waitqueue_head(&mr->mmkey.wait); -static void mlx5r_mkeys_uninit(struct mlx5_cache_ent *ent) -{ - struct mlx5_mkeys_page *page; + mr->ibmr.frmr.key = key; + ret = ib_frmr_pool_pop(&dev->ib_dev, &mr->ibmr); + if (ret) { + kfree(mr); + return ERR_PTR(ret); + } + mr->mmkey.key = mr->ibmr.frmr.handle; + mr->mmkey.type = MLX5_MKEY_MR; - WARN_ON(ent->mkeys_queue.ci || ent->mkeys_queue.num_pages > 1); - page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - list_del(&page->list); - kfree(page); + return mr; } -struct mlx5_cache_ent * -mlx5r_cache_create_ent_locked(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key, - bool persistent_entry) +static int mlx5r_create_mkeys(struct ib_device *device, struct ib_frmr_key *key, + u32 *handles, unsigned int count) { - struct mlx5_cache_ent *ent; - int order; - int ret; + int access_mode = + key->vendor_key & MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK ? + MLX5_MKC_ACCESS_MODE_KSM : + MLX5_MKC_ACCESS_MODE_MTT; - ent = kzalloc(sizeof(*ent), GFP_KERNEL); - if (!ent) - return ERR_PTR(-ENOMEM); + struct mlx5_ib_dev *dev = to_mdev(device); + size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); + u16 st_index; + void *mkc; + u32 *in; + int err, i; + u8 ph; - ret = mlx5r_mkeys_init(ent); - if (ret) - goto mkeys_err; - ent->rb_key = rb_key; - ent->dev = dev; - ent->is_tmp = !persistent_entry; + in = kzalloc(inlen, GFP_KERNEL); + if (!in) + return -ENOMEM; + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func); + set_mkc_access_pd_addr_fields(mkc, key->access_flags, 0, dev->umrc.pd); + MLX5_SET(mkc, mkc, free, 1); + MLX5_SET(mkc, mkc, umr_en, 1); + MLX5_SET(mkc, mkc, access_mode_1_0, access_mode & 0x3); + MLX5_SET(mkc, mkc, access_mode_4_2, (access_mode >> 2) & 0x7); + MLX5_SET(mkc, mkc, ma_translation_mode, !!key->ats); + MLX5_SET(mkc, mkc, translations_octword_size, + get_mkc_octo_size(access_mode, key->num_dma_blocks)); + MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); - ret = mlx5_cache_ent_insert(&dev->cache, ent); - if (ret) - goto ent_insert_err; - - if (persistent_entry) { - if (rb_key.access_mode == MLX5_MKC_ACCESS_MODE_KSM) - order = MLX5_IMR_KSM_CACHE_ENTRY; - else - order = order_base_2(rb_key.ndescs) - 2; - - if ((dev->mdev->profile.mask & MLX5_PROF_MASK_MR_CACHE) && - !dev->is_rep && mlx5_core_is_pf(dev->mdev) && - mlx5r_umr_can_load_pas(dev, 0)) - ent->limit = dev->mdev->profile.mr_cache[order].limit; - else - ent->limit = 0; - - mlx5_mkey_cache_debugfs_add_ent(dev, ent); + st_index = key->kernel_vendor_key & + MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK; + ph = key->kernel_vendor_key & MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK; + if (ph) { + /* Normalize ph: swap MLX5_IB_NO_PH for 0 */ + if (ph == MLX5_IB_NO_PH) + ph = 0; + MLX5_SET(mkc, mkc, pcie_tph_en, 1); + MLX5_SET(mkc, mkc, pcie_tph_ph, ph); + if (st_index != MLX5_MKC_PCIE_TPH_NO_STEERING_TAG_INDEX) + MLX5_SET(mkc, mkc, pcie_tph_steering_tag_index, + st_index); } - return ent; -ent_insert_err: - mlx5r_mkeys_uninit(ent); -mkeys_err: - kfree(ent); - return ERR_PTR(ret); -} - -static void mlx5r_destroy_cache_entries(struct mlx5_ib_dev *dev) -{ - struct rb_root *root = &dev->cache.rb_root; - struct mlx5_cache_ent *ent; - struct rb_node *node; - - mutex_lock(&dev->cache.rb_lock); - node = rb_first(root); - while (node) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - node = rb_next(node); - clean_keys(dev, ent); - rb_erase(&ent->node, root); - mlx5r_mkeys_uninit(ent); - kfree(ent); + for (i = 0; i < count; i++) { + assign_mkey_variant(dev, handles + i, in); + err = mlx5_core_create_mkey(dev->mdev, handles + i, in, inlen); + if (err) + goto free_in; } - mutex_unlock(&dev->cache.rb_lock); +free_in: + kfree(in); + if (err) + for (; i > 0; i--) + mlx5_core_destroy_mkey(dev->mdev, handles[i]); + return err; } -int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev) +static void mlx5r_destroy_mkeys(struct ib_device *device, u32 *handles, + unsigned int count) { - struct mlx5_mkey_cache *cache = &dev->cache; - struct rb_root *root = &dev->cache.rb_root; - struct mlx5r_cache_rb_key rb_key = { - .access_mode = MLX5_MKC_ACCESS_MODE_MTT, - .ph = MLX5_IB_NO_PH, - }; - struct mlx5_cache_ent *ent; - struct rb_node *node; - int ret; - int i; + struct mlx5_ib_dev *dev = to_mdev(device); + int i, err; - mutex_init(&dev->slow_path_mutex); - mutex_init(&dev->cache.rb_lock); - dev->cache.rb_root = RB_ROOT; - cache->wq = alloc_ordered_workqueue("mkey_cache", WQ_MEM_RECLAIM); - if (!cache->wq) { - mlx5_ib_warn(dev, "failed to create work queue\n"); - return -ENOMEM; + for (i = 0; i < count; i++) { + err = mlx5_core_destroy_mkey(dev->mdev, handles[i]); + if (err) + pr_warn_ratelimited( + "mlx5_ib: failed to destroy mkey %d: %d", + handles[i], err); } +} - timer_setup(&dev->delay_timer, delay_time_func, 0); - mlx5_mkey_cache_debugfs_init(dev); - mutex_lock(&cache->rb_lock); - for (i = 0; i <= mkey_cache_max_order(dev); i++) { - rb_key.ndescs = MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS << i; - ent = mlx5r_cache_create_ent_locked(dev, rb_key, true); - if (IS_ERR(ent)) { - ret = PTR_ERR(ent); - goto err; - } - } +static int mlx5r_build_frmr_key(struct ib_device *device, + const struct ib_frmr_key *in, + struct ib_frmr_key *out) +{ + struct mlx5_ib_dev *dev = to_mdev(device); - ret = mlx5_odp_init_mkey_cache(dev); - if (ret) - goto err; + /* check HW capabilities of users requested frmr key */ + if ((in->ats && !MLX5_CAP_GEN(dev->mdev, ats)) || + ilog2(in->num_dma_blocks) > mkey_max_umr_order(dev)) + return -EOPNOTSUPP; - mutex_unlock(&cache->rb_lock); - for (node = rb_first(root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - } + if (in->vendor_key & ~MLX5_FRMR_POOLS_KEY_VENDOR_KEY_SUPPORTED) + return -EOPNOTSUPP; - return 0; + out->ats = in->ats; + out->access_flags = + get_unchangeable_access_flags(dev, in->access_flags); + out->vendor_key = in->vendor_key; + out->num_dma_blocks = in->num_dma_blocks; -err: - mutex_unlock(&cache->rb_lock); - mlx5_mkey_cache_debugfs_cleanup(dev); - mlx5r_destroy_cache_entries(dev); - destroy_workqueue(cache->wq); - mlx5_ib_warn(dev, "failed to create mkey cache entry\n"); - return ret; + return 0; } -void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev) -{ - struct rb_root *root = &dev->cache.rb_root; - struct mlx5_cache_ent *ent; - struct rb_node *node; - - if (!dev->cache.wq) - return; - - mutex_lock(&dev->cache.rb_lock); - for (node = rb_first(root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - ent->disabled = true; - spin_unlock_irq(&ent->mkeys_queue.lock); - cancel_delayed_work(&ent->dwork); - } - mutex_unlock(&dev->cache.rb_lock); - - /* - * After all entries are disabled and will not reschedule on WQ, - * flush it and all async commands. - */ - flush_workqueue(dev->cache.wq); +static struct ib_frmr_pool_ops mlx5r_frmr_pool_ops = { + .create_frmrs = mlx5r_create_mkeys, + .destroy_frmrs = mlx5r_destroy_mkeys, + .build_key = mlx5r_build_frmr_key, +}; - mlx5_mkey_cache_debugfs_cleanup(dev); +int mlx5r_frmr_pools_init(struct ib_device *device) +{ + struct mlx5_ib_dev *dev = to_mdev(device); - /* At this point all entries are disabled and have no concurrent work. */ - mlx5r_destroy_cache_entries(dev); + mutex_init(&dev->slow_path_mutex); + return ib_frmr_pools_init(device, &mlx5r_frmr_pool_ops); +} - destroy_workqueue(dev->cache.wq); - timer_delete_sync(&dev->delay_timer); +void mlx5r_frmr_pools_cleanup(struct ib_device *device) +{ + ib_frmr_pools_cleanup(device); } struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc) @@ -1107,13 +417,6 @@ static int get_octo_len(u64 addr, u64 len, int page_shift) return (npages + 1) / 2; } -static int mkey_cache_max_order(struct mlx5_ib_dev *dev) -{ - if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) - return MKEY_CACHE_LAST_STD_ENTRY; - return MLX5_MAX_UMR_SHIFT; -} - static void set_mr_fields(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr, u64 length, int access_flags, u64 iova) { @@ -1142,8 +445,6 @@ static struct mlx5_ib_mr *alloc_cacheable_mr(struct ib_pd *pd, u16 st_index, u8 ph) { struct mlx5_ib_dev *dev = to_mdev(pd->device); - struct mlx5r_cache_rb_key rb_key = {}; - struct mlx5_cache_ent *ent; struct mlx5_ib_mr *mr; unsigned long page_size; @@ -1155,33 +456,12 @@ static struct mlx5_ib_mr *alloc_cacheable_mr(struct ib_pd *pd, if (WARN_ON(!page_size)) return ERR_PTR(-EINVAL); - rb_key.access_mode = access_mode; - rb_key.ndescs = ib_umem_num_dma_blocks(umem, page_size); - rb_key.ats = mlx5_umem_needs_ats(dev, umem, access_flags); - rb_key.access_flags = get_unchangeable_access_flags(dev, access_flags); - rb_key.st_index = st_index; - rb_key.ph = ph; - ent = mkey_cache_ent_from_rb_key(dev, rb_key); - /* - * If the MR can't come from the cache then synchronously create an uncached - * one. - */ - if (!ent) { - mutex_lock(&dev->slow_path_mutex); - mr = reg_create(pd, umem, iova, access_flags, page_size, false, access_mode, - st_index, ph); - mutex_unlock(&dev->slow_path_mutex); - if (IS_ERR(mr)) - return mr; - mr->mmkey.rb_key = rb_key; - mr->mmkey.cacheable = true; - return mr; - } - - mr = _mlx5_mr_cache_alloc(dev, ent); + mr = _mlx5_frmr_pool_alloc(dev, umem, access_flags, access_mode, + page_size, st_index, ph); if (IS_ERR(mr)) return mr; + mr->mmkey.type = MLX5_MKEY_MR; mr->ibmr.pd = pd; mr->umem = umem; mr->page_shift = order_base_2(page_size); @@ -1810,18 +1090,24 @@ static bool can_use_umr_rereg_pas(struct mlx5_ib_mr *mr, unsigned long *page_size) { struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); + u8 access_mode; - /* We only track the allocated sizes of MRs from the cache */ - if (!mr->mmkey.cache_ent) + /* We only track the allocated sizes of MRs from the frmr pools */ + if (!mr->ibmr.frmr.pool) return false; if (!mlx5r_umr_can_load_pas(dev, new_umem->length)) return false; - *page_size = mlx5_umem_mkc_find_best_pgsz( - dev, new_umem, iova, mr->mmkey.cache_ent->rb_key.access_mode); + access_mode = mr->ibmr.frmr.key.vendor_key & + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK ? + MLX5_MKC_ACCESS_MODE_KSM : + MLX5_MKC_ACCESS_MODE_MTT; + + *page_size = + mlx5_umem_mkc_find_best_pgsz(dev, new_umem, iova, access_mode); if (WARN_ON(!*page_size)) return false; - return (mr->mmkey.cache_ent->rb_key.ndescs) >= + return (mr->ibmr.frmr.key.num_dma_blocks) >= ib_umem_num_dma_blocks(new_umem, *page_size); } @@ -1882,7 +1168,8 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, int err; if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM) || mr->data_direct || - mr->mmkey.rb_key.ph != MLX5_IB_NO_PH) + (mr->ibmr.frmr.key.kernel_vendor_key & + MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK) != 0) return ERR_PTR(-EOPNOTSUPP); mlx5_ib_dbg( @@ -2023,47 +1310,6 @@ mlx5_free_priv_descs(struct mlx5_ib_mr *mr) } } -static int cache_ent_find_and_store(struct mlx5_ib_dev *dev, - struct mlx5_ib_mr *mr) -{ - struct mlx5_mkey_cache *cache = &dev->cache; - struct mlx5_cache_ent *ent; - int ret; - - if (mr->mmkey.cache_ent) { - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - goto end; - } - - mutex_lock(&cache->rb_lock); - ent = mkey_cache_ent_from_rb_key(dev, mr->mmkey.rb_key); - if (ent) { - if (ent->rb_key.ndescs == mr->mmkey.rb_key.ndescs) { - if (ent->disabled) { - mutex_unlock(&cache->rb_lock); - return -EOPNOTSUPP; - } - mr->mmkey.cache_ent = ent; - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - mutex_unlock(&cache->rb_lock); - goto end; - } - } - - ent = mlx5r_cache_create_ent_locked(dev, mr->mmkey.rb_key, false); - mutex_unlock(&cache->rb_lock); - if (IS_ERR(ent)) - return PTR_ERR(ent); - - mr->mmkey.cache_ent = ent; - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - -end: - ret = push_mkey_locked(mr->mmkey.cache_ent, mr->mmkey.key); - spin_unlock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - return ret; -} - static int mlx5_ib_revoke_data_direct_mr(struct mlx5_ib_mr *mr) { struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); @@ -2129,33 +1375,12 @@ static int mlx5r_handle_mkey_cleanup(struct mlx5_ib_mr *mr) bool is_odp_dma_buf = is_dmabuf_mr(mr) && !to_ib_umem_dmabuf(mr->umem)->pinned; struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); - struct mlx5_cache_ent *ent = mr->mmkey.cache_ent; bool is_odp = is_odp_mr(mr); - bool from_cache = !!ent; int ret; - if (mr->mmkey.cacheable && !mlx5_umr_revoke_mr_with_lock(mr) && - !cache_ent_find_and_store(dev, mr)) { - ent = mr->mmkey.cache_ent; - /* upon storing to a clean temp entry - schedule its cleanup */ - spin_lock_irq(&ent->mkeys_queue.lock); - if (from_cache) - ent->in_use--; - if (ent->is_tmp && !ent->tmp_cleanup_scheduled) { - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, - secs_to_jiffies(30)); - ent->tmp_cleanup_scheduled = true; - } - spin_unlock_irq(&ent->mkeys_queue.lock); + if (mr->ibmr.frmr.pool && !mlx5_umr_revoke_mr_with_lock(mr) && + !ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr)) return 0; - } - - if (ent) { - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use--; - mr->mmkey.cache_ent = NULL; - spin_unlock_irq(&ent->mkeys_queue.lock); - } if (is_odp) mutex_lock(&to_ib_umem_odp(mr->umem)->umem_mutex); @@ -2239,7 +1464,7 @@ static int __mlx5_ib_dereg_mr(struct ib_mr *ibmr) mlx5_ib_free_odp_mr(mr); } - if (!mr->mmkey.cache_ent) + if (!mr->ibmr.frmr.pool) mlx5_free_priv_descs(mr); kfree(mr); diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index e71ee3d52eb04..c6c60b9ef1805 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1875,25 +1875,6 @@ mlx5_ib_odp_destroy_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq) return err; } -int mlx5_odp_init_mkey_cache(struct mlx5_ib_dev *dev) -{ - struct mlx5r_cache_rb_key rb_key = { - .access_mode = MLX5_MKC_ACCESS_MODE_KSM, - .ndescs = mlx5_imr_ksm_entries, - .ph = MLX5_IB_NO_PH, - }; - struct mlx5_cache_ent *ent; - - if (!(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) - return 0; - - ent = mlx5r_cache_create_ent_locked(dev, rb_key, true); - if (IS_ERR(ent)) - return PTR_ERR(ent); - - return 0; -} - static const struct ib_device_ops mlx5_ib_dev_odp_ops = { .advise_mr = mlx5_ib_advise_mr, }; diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index e9361f0140e7b..7eeaf6a94c974 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -9,6 +9,7 @@ #define MLX5_MAX_UMR_SHIFT 16 #define MLX5_MAX_UMR_PAGES (1 << MLX5_MAX_UMR_SHIFT) +#define MLX5_MAX_UMR_EXTENDED_SHIFT 43 #define MLX5_IB_UMR_OCTOWORD 16 #define MLX5_IB_UMR_XLT_ALIGNMENT 64 From c3e89e87469f4d66bc98f935cd30d3d4f52b3b9d Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:42 +0200 Subject: [PATCH 191/214] net/mlx5: Drop MR cache related code Following mlx5_ib move to using FRMR pools, drop all unused code of MR cache. Signed-off-by: Michael Guralnik Reviewed-by: Yishai Hadas Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- .../net/ethernet/mellanox/mlx5/core/main.c | 67 +------------------ include/linux/mlx5/driver.h | 11 --- 2 files changed, 1 insertion(+), 77 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 4209da722f9a6..4f1e2bd970179 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -110,74 +110,9 @@ static struct mlx5_profile profile[] = { }, [2] = { - .mask = MLX5_PROF_MASK_QP_SIZE | - MLX5_PROF_MASK_MR_CACHE, + .mask = MLX5_PROF_MASK_QP_SIZE, .log_max_qp = LOG_MAX_SUPPORTED_QPS, .num_cmd_caches = MLX5_NUM_COMMAND_CACHES, - .mr_cache[0] = { - .size = 500, - .limit = 250 - }, - .mr_cache[1] = { - .size = 500, - .limit = 250 - }, - .mr_cache[2] = { - .size = 500, - .limit = 250 - }, - .mr_cache[3] = { - .size = 500, - .limit = 250 - }, - .mr_cache[4] = { - .size = 500, - .limit = 250 - }, - .mr_cache[5] = { - .size = 500, - .limit = 250 - }, - .mr_cache[6] = { - .size = 500, - .limit = 250 - }, - .mr_cache[7] = { - .size = 500, - .limit = 250 - }, - .mr_cache[8] = { - .size = 500, - .limit = 250 - }, - .mr_cache[9] = { - .size = 500, - .limit = 250 - }, - .mr_cache[10] = { - .size = 500, - .limit = 250 - }, - .mr_cache[11] = { - .size = 500, - .limit = 250 - }, - .mr_cache[12] = { - .size = 64, - .limit = 32 - }, - .mr_cache[13] = { - .size = 32, - .limit = 16 - }, - .mr_cache[14] = { - .size = 16, - .limit = 8 - }, - .mr_cache[15] = { - .size = 8, - .limit = 4 - }, }, [3] = { .mask = MLX5_PROF_MASK_QP_SIZE, diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 1c54aa6f74fbc..e13a206408030 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -705,23 +705,12 @@ struct mlx5_st; enum { MLX5_PROF_MASK_QP_SIZE = (u64)1 << 0, - MLX5_PROF_MASK_MR_CACHE = (u64)1 << 1, -}; - -enum { - MKEY_CACHE_LAST_STD_ENTRY = 20, - MLX5_IMR_KSM_CACHE_ENTRY, - MAX_MKEY_CACHE_ENTRIES }; struct mlx5_profile { u64 mask; u8 log_max_qp; u8 num_cmd_caches; - struct { - int size; - int limit; - } mr_cache[MAX_MKEY_CACHE_ENTRIES]; }; struct mlx5_hca_cap { From 5da5052632fb9cc2173aa0e0bf2c64155e653094 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:43 +0200 Subject: [PATCH 192/214] RDMA/nldev: Add command to get FRMR pools Add support for a new command in netlink to dump to user the state of the FRMR pools on the devices. Expose each pool with its key and the usage statistics for it. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/nldev.c | 165 +++++++++++++++++++++++++++++++ include/uapi/rdma/rdma_netlink.h | 17 ++++ 2 files changed, 182 insertions(+) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 2220a2dfab240..6637c76165be2 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -37,11 +37,13 @@ #include #include #include +#include #include "core_priv.h" #include "cma_priv.h" #include "restrack.h" #include "uverbs.h" +#include "frmr_pools.h" /* * This determines whether a non-privileged user is allowed to specify a @@ -172,6 +174,16 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_NAME_ASSIGN_TYPE] = { .type = NLA_U8 }, [RDMA_NLDEV_ATTR_EVENT_TYPE] = { .type = NLA_U8 }, [RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED] = { .type = NLA_U8 }, + [RDMA_NLDEV_ATTR_FRMR_POOLS] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS] = { .type = NLA_U8 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE] = { .type = NLA_U64 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -2637,6 +2649,156 @@ static int nldev_deldev(struct sk_buff *skb, struct nlmsghdr *nlh, return ib_del_sub_device_and_put(device); } +static int fill_frmr_pool_key(struct sk_buff *msg, struct ib_frmr_key *key) +{ + struct nlattr *key_attr; + + key_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY); + if (!key_attr) + return -EMSGSIZE; + + if (nla_put_u8(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS, key->ats)) + goto err; + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS, + key->access_flags)) + goto err; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY, + key->vendor_key, RDMA_NLDEV_ATTR_PAD)) + goto err; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS, + key->num_dma_blocks, RDMA_NLDEV_ATTR_PAD)) + goto err; + + nla_nest_end(msg, key_attr); + return 0; + +err: + return -EMSGSIZE; +} + +static int fill_frmr_pool_entry(struct sk_buff *msg, struct ib_frmr_pool *pool) +{ + if (fill_frmr_pool_key(msg, &pool->key)) + return -EMSGSIZE; + + spin_lock(&pool->lock); + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, + pool->queue.ci + pool->inactive_queue.ci)) + goto err_unlock; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, + pool->max_in_use, RDMA_NLDEV_ATTR_PAD)) + goto err_unlock; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, + pool->in_use, RDMA_NLDEV_ATTR_PAD)) + goto err_unlock; + spin_unlock(&pool->lock); + + return 0; + +err_unlock: + spin_unlock(&pool->lock); + return -EMSGSIZE; +} + +static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; + struct ib_frmr_pools *pools; + int err, ret = 0, idx = 0; + struct ib_frmr_pool *pool; + struct nlattr *table_attr; + struct nlattr *entry_attr; + struct ib_device *device; + int start = cb->args[0]; + struct rb_node *node; + struct nlmsghdr *nlh; + bool filled = false; + + err = __nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, + nldev_policy, NL_VALIDATE_LIBERAL, NULL); + if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) + return -EINVAL; + + device = ib_device_get_by_index( + sock_net(skb->sk), nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX])); + if (!device) + return -EINVAL; + + pools = device->frmr_pools; + if (!pools) { + ib_device_put(device); + return 0; + } + + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, + RDMA_NLDEV_CMD_FRMR_POOLS_GET), + 0, NLM_F_MULTI); + + if (!nlh || fill_nldev_handle(skb, device)) { + ret = -EMSGSIZE; + goto err; + } + + table_attr = nla_nest_start_noflag(skb, RDMA_NLDEV_ATTR_FRMR_POOLS); + if (!table_attr) { + ret = -EMSGSIZE; + goto err; + } + + read_lock(&pools->rb_lock); + for (node = rb_first(&pools->rb_root); node; node = rb_next(node)) { + pool = rb_entry(node, struct ib_frmr_pool, node); + if (pool->key.kernel_vendor_key) + continue; + + if (idx < start) { + idx++; + continue; + } + + filled = true; + + entry_attr = nla_nest_start_noflag( + skb, RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY); + if (!entry_attr) { + ret = -EMSGSIZE; + goto end_msg; + } + + if (fill_frmr_pool_entry(skb, pool)) { + nla_nest_cancel(skb, entry_attr); + ret = -EMSGSIZE; + goto end_msg; + } + + nla_nest_end(skb, entry_attr); + idx++; + } +end_msg: + read_unlock(&pools->rb_lock); + + nla_nest_end(skb, table_attr); + nlmsg_end(skb, nlh); + cb->args[0] = idx; + + /* + * No more entries to fill, cancel the message and + * return 0 to mark end of dumpit. + */ + if (!filled) + goto err; + + ib_device_put(device); + return skb->len; + +err: + nlmsg_cancel(skb, nlh); + ib_device_put(device); + return ret; +} + static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { [RDMA_NLDEV_CMD_GET] = { .doit = nldev_get_doit, @@ -2743,6 +2905,9 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { .doit = nldev_deldev, .flags = RDMA_NL_ADMIN_PERM, }, + [RDMA_NLDEV_CMD_FRMR_POOLS_GET] = { + .dump = nldev_frmr_pools_get_dumpit, + }, }; static int fill_mon_netdev_rename(struct sk_buff *msg, diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index f41f0228fcd0e..8f17ffe0190cb 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -308,6 +308,8 @@ enum rdma_nldev_command { RDMA_NLDEV_CMD_MONITOR, + RDMA_NLDEV_CMD_FRMR_POOLS_GET, /* can dump */ + RDMA_NLDEV_NUM_OPS }; @@ -582,6 +584,21 @@ enum rdma_nldev_attr { RDMA_NLDEV_SYS_ATTR_MONITOR_MODE, /* u8 */ RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED, /* u8 */ + + /* + * FRMR Pools attributes + */ + RDMA_NLDEV_ATTR_FRMR_POOLS, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS, /* u8 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */ + /* * Always the end */ From 5a2273aa69d0ba577fa37c66ed480172fa04ec9f Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:44 +0200 Subject: [PATCH 193/214] RDMA/core: Add netlink command to modify FRMR aging Allow users to set FRMR pools aging timer through netlink. This functionality will allow user to control how long handles reside in the kernel before being destroyed, thus being able to tune the tradeoff between memory and HW object consumption and memory registration optimization. Since FRMR pools is highly beneficial for application restart scenarios, this command allows users to modify the aging timer to their application restart time, making sure the FRMR handles deregistered on application teardown are kept for long enough in the pools for reuse in the application startup. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/frmr_pools.c | 31 +++++++++++++++++++++-- drivers/infiniband/core/frmr_pools.h | 2 ++ drivers/infiniband/core/nldev.c | 37 ++++++++++++++++++++++++++++ include/uapi/rdma/rdma_netlink.h | 3 +++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c index febe23920f562..c688529a16d8b 100644 --- a/drivers/infiniband/core/frmr_pools.c +++ b/drivers/infiniband/core/frmr_pools.c @@ -174,7 +174,7 @@ static void pool_aging_work(struct work_struct *work) if (has_work) queue_delayed_work( pools->aging_wq, &pool->aging_work, - secs_to_jiffies(FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS)); + secs_to_jiffies(READ_ONCE(pools->aging_period_sec))); } static void destroy_frmr_pool(struct ib_device *device, @@ -214,6 +214,8 @@ int ib_frmr_pools_init(struct ib_device *device, return -ENOMEM; } + pools->aging_period_sec = FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS; + device->frmr_pools = pools; return 0; } @@ -249,6 +251,31 @@ void ib_frmr_pools_cleanup(struct ib_device *device) } EXPORT_SYMBOL(ib_frmr_pools_cleanup); +int ib_frmr_pools_set_aging_period(struct ib_device *device, u32 period_sec) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + struct rb_node *node; + + if (!pools) + return -EINVAL; + + if (period_sec == 0) + return -EINVAL; + + WRITE_ONCE(pools->aging_period_sec, period_sec); + + read_lock(&pools->rb_lock); + for (node = rb_first(&pools->rb_root); node; node = rb_next(node)) { + pool = rb_entry(node, struct ib_frmr_pool, node); + mod_delayed_work(pools->aging_wq, &pool->aging_work, + secs_to_jiffies(period_sec)); + } + read_unlock(&pools->rb_lock); + + return 0; +} + static int compare_keys(struct ib_frmr_key *key1, struct ib_frmr_key *key2) { int res; @@ -523,7 +550,7 @@ int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) if (ret == 0 && schedule_aging) queue_delayed_work(pools->aging_wq, &pool->aging_work, - secs_to_jiffies(FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS)); + secs_to_jiffies(READ_ONCE(pools->aging_period_sec))); return ret; } diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h index b144273ee3478..81149ff15e003 100644 --- a/drivers/infiniband/core/frmr_pools.h +++ b/drivers/infiniband/core/frmr_pools.h @@ -54,8 +54,10 @@ struct ib_frmr_pools { const struct ib_frmr_pool_ops *pool_ops; struct workqueue_struct *aging_wq; + u32 aging_period_sec; }; int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, u32 pinned_handles); +int ib_frmr_pools_set_aging_period(struct ib_device *device, u32 period_sec); #endif /* RDMA_CORE_FRMR_POOLS_H */ diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 6637c76165be2..8d004b7568b7b 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -184,6 +184,7 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE] = { .type = NLA_U64 }, [RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -2799,6 +2800,38 @@ static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, return ret; } +static int nldev_frmr_pools_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; + struct ib_device *device; + u32 aging_period; + int err; + + err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy, + extack); + if (err) + return err; + + if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX]) + return -EINVAL; + + if (!tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]) + return -EINVAL; + + device = ib_device_get_by_index( + sock_net(skb->sk), nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX])); + if (!device) + return -EINVAL; + + aging_period = nla_get_u32(tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]); + + err = ib_frmr_pools_set_aging_period(device, aging_period); + + ib_device_put(device); + return err; +} + static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { [RDMA_NLDEV_CMD_GET] = { .doit = nldev_get_doit, @@ -2908,6 +2941,10 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { [RDMA_NLDEV_CMD_FRMR_POOLS_GET] = { .dump = nldev_frmr_pools_get_dumpit, }, + [RDMA_NLDEV_CMD_FRMR_POOLS_SET] = { + .doit = nldev_frmr_pools_set_doit, + .flags = RDMA_NL_ADMIN_PERM, + }, }; static int fill_mon_netdev_rename(struct sk_buff *msg, diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 8f17ffe0190cb..f9c295caf2b16 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -310,6 +310,8 @@ enum rdma_nldev_command { RDMA_NLDEV_CMD_FRMR_POOLS_GET, /* can dump */ + RDMA_NLDEV_CMD_FRMR_POOLS_SET, + RDMA_NLDEV_NUM_OPS }; @@ -598,6 +600,7 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, /* u32 */ RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, /* u64 */ RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD, /* u32 */ /* * Always the end From 11b443892866e8d53b661c1c315f4933538f69cc Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:45 +0200 Subject: [PATCH 194/214] RDMA/nldev: Add command to set pinned FRMR handles Allow users to set through netlink, for a specific FRMR pool, the amount of handles that are not aged, and fill the pool to this amount. This allows users to warm-up the FRMR pools to an expected amount of handles with specific attributes that fits their expected usage. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/nldev.c | 88 ++++++++++++++++++++++++++++---- include/uapi/rdma/rdma_netlink.h | 1 + 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 8d004b7568b7b..0b0f689eadd76 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -185,6 +185,7 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE] = { .type = NLA_U64 }, [RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE] = { .type = NLA_U64 }, [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES] = { .type = NLA_U32 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -2692,6 +2693,9 @@ static int fill_frmr_pool_entry(struct sk_buff *msg, struct ib_frmr_pool *pool) if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, pool->in_use, RDMA_NLDEV_ATTR_PAD)) goto err_unlock; + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, + pool->pinned_handles)) + goto err_unlock; spin_unlock(&pool->lock); return 0; @@ -2701,6 +2705,54 @@ static int fill_frmr_pool_entry(struct sk_buff *msg, struct ib_frmr_pool *pool) return -EMSGSIZE; } +static void nldev_frmr_pools_parse_key(struct nlattr *tb[], + struct ib_frmr_key *key, + struct netlink_ext_ack *extack) +{ + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]) + key->ats = nla_get_u8(tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS]) + key->access_flags = nla_get_u32( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY]) + key->vendor_key = nla_get_u64( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]) + key->num_dma_blocks = nla_get_u64( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]); +} + +static int nldev_frmr_pools_set_pinned(struct ib_device *device, + struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct nlattr *key_tb[RDMA_NLDEV_ATTR_MAX]; + struct ib_frmr_key key = { 0 }; + u32 pinned_handles = 0; + int err = 0; + + pinned_handles = + nla_get_u32(tb[RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES]); + + if (!tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY]) + return -EINVAL; + + err = nla_parse_nested(key_tb, RDMA_NLDEV_ATTR_MAX - 1, + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY], nldev_policy, + extack); + if (err) + return err; + + nldev_frmr_pools_parse_key(key_tb, &key, extack); + + err = ib_frmr_pools_set_pinned(device, &key, pinned_handles); + + return err; +} + static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { @@ -2803,32 +2855,46 @@ static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, static int nldev_frmr_pools_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { - struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; struct ib_device *device; + struct nlattr **tb; u32 aging_period; int err; + tb = kcalloc(RDMA_NLDEV_ATTR_MAX, sizeof(*tb), GFP_KERNEL); + if (!tb) + return -ENOMEM; + err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy, extack); if (err) - return err; - - if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX]) - return -EINVAL; + goto free_tb; - if (!tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]) - return -EINVAL; + if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX]) { + err = -EINVAL; + goto free_tb; + } device = ib_device_get_by_index( sock_net(skb->sk), nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX])); - if (!device) - return -EINVAL; + if (!device) { + err = -EINVAL; + goto free_tb; + } - aging_period = nla_get_u32(tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]); + if (tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]) { + aging_period = nla_get_u32( + tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]); + err = ib_frmr_pools_set_aging_period(device, aging_period); + goto done; + } - err = ib_frmr_pools_set_aging_period(device, aging_period); + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES]) + err = nldev_frmr_pools_set_pinned(device, tb, extack); +done: ib_device_put(device); +free_tb: + kfree(tb); return err; } diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index f9c295caf2b16..39178df104f01 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -601,6 +601,7 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, /* u64 */ RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */ RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */ /* * Always the end From abcdc5ac189dad68abe21d6c0d0be256b1b7e411 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Mon, 22 Dec 2025 14:40:46 +0200 Subject: [PATCH 195/214] RDMA/nldev: Expose kernel-internal FRMR pools in netlink Allow netlink users, through the usage of driver-details netlink attribute, to get information about internal FRMR pools that use the kernel_vendor_key FRMR key member. Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Edward Srouji Signed-off-by: NipaLocal --- drivers/infiniband/core/nldev.c | 28 +++++++++++++++++++++++----- include/uapi/rdma/rdma_netlink.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 0b0f689eadd76..80b0079f63ae8 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -186,6 +186,7 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE] = { .type = NLA_U64 }, [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, [RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY] = { .type = NLA_U64 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -2671,6 +2672,12 @@ static int fill_frmr_pool_key(struct sk_buff *msg, struct ib_frmr_key *key) key->num_dma_blocks, RDMA_NLDEV_ATTR_PAD)) goto err; + if (key->kernel_vendor_key && + nla_put_u64_64bit(msg, + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, + key->kernel_vendor_key, RDMA_NLDEV_ATTR_PAD)) + goto err; + nla_nest_end(msg, key_attr); return 0; @@ -2705,9 +2712,9 @@ static int fill_frmr_pool_entry(struct sk_buff *msg, struct ib_frmr_pool *pool) return -EMSGSIZE; } -static void nldev_frmr_pools_parse_key(struct nlattr *tb[], - struct ib_frmr_key *key, - struct netlink_ext_ack *extack) +static int nldev_frmr_pools_parse_key(struct nlattr *tb[], + struct ib_frmr_key *key, + struct netlink_ext_ack *extack) { if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]) key->ats = nla_get_u8(tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]); @@ -2723,6 +2730,11 @@ static void nldev_frmr_pools_parse_key(struct nlattr *tb[], if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]) key->num_dma_blocks = nla_get_u64( tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY]) + return -EINVAL; + + return 0; } static int nldev_frmr_pools_set_pinned(struct ib_device *device, @@ -2746,7 +2758,9 @@ static int nldev_frmr_pools_set_pinned(struct ib_device *device, if (err) return err; - nldev_frmr_pools_parse_key(key_tb, &key, extack); + err = nldev_frmr_pools_parse_key(key_tb, &key, extack); + if (err) + return err; err = ib_frmr_pools_set_pinned(device, &key, pinned_handles); @@ -2762,6 +2776,7 @@ static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, struct ib_frmr_pool *pool; struct nlattr *table_attr; struct nlattr *entry_attr; + bool show_details = false; struct ib_device *device; int start = cb->args[0]; struct rb_node *node; @@ -2778,6 +2793,9 @@ static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, if (!device) return -EINVAL; + if (tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]) + show_details = nla_get_u8(tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]); + pools = device->frmr_pools; if (!pools) { ib_device_put(device); @@ -2803,7 +2821,7 @@ static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, read_lock(&pools->rb_lock); for (node = rb_first(&pools->rb_root); node; node = rb_next(node)) { pool = rb_entry(node, struct ib_frmr_pool, node); - if (pool->key.kernel_vendor_key) + if (pool->key.kernel_vendor_key && !show_details) continue; if (idx < start) { diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index 39178df104f01..aac9782ddc09a 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -602,6 +602,7 @@ enum rdma_nldev_attr { RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */ RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD, /* u32 */ RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */ /* * Always the end From 025429790ecd8a8f7a21d53be6e1520fad345061 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 22 Dec 2025 20:42:19 +0800 Subject: [PATCH 196/214] ipv6: fix a BUG in rt6_get_pcpu_route() under PREEMPT_RT On PREEMPT_RT kernels, after rt6_get_pcpu_route() returns NULL, the current task can be preempted. Another task running on the same CPU may then execute rt6_make_pcpu_route() and successfully install a pcpu_rt entry. When the first task resumes execution, its cmpxchg() in rt6_make_pcpu_route() will fail because rt6i_pcpu is no longer NULL, triggering the BUG_ON(prev). It's easy to reproduce it by adding mdelay() after rt6_get_pcpu_route(). Using preempt_disable/enable is not appropriate here because ip6_rt_pcpu_alloc() may sleep. Fix this by handling the cmpxchg() failure gracefully on PREEMPT_RT: free our allocation and return the existing pcpu_rt installed by another task. The BUG_ON is retained for non-PREEMPT_RT kernels where such races should not occur. Link: https://syzkaller.appspot.com/bug?extid=9b35e9bc0951140d13e6 Fixes: 8b1c04acad08 ("softirq: Make softirq control and processing RT aware") Reported-by: syzbot+9b35e9bc0951140d13e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6918cd88.050a0220.1c914e.0045.GAE@google.com/T/ Signed-off-by: Jiayuan Chen Signed-off-by: NipaLocal --- net/ipv6/route.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index aee6a10b112aa..9e7afda7cba2b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1470,7 +1470,18 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net, p = this_cpu_ptr(res->nh->rt6i_pcpu); prev = cmpxchg(p, NULL, pcpu_rt); - BUG_ON(prev); + if (unlikely(prev)) { + /* + * Another task on this CPU already installed a pcpu_rt. + * This can happen on PREEMPT_RT where preemption is possible. + * Free our allocation and return the existing one. + */ + BUG_ON(!IS_ENABLED(CONFIG_PREEMPT_RT)); + + dst_dev_put(&pcpu_rt->dst); + dst_release(&pcpu_rt->dst); + return prev; + } if (res->f6i->fib6_destroying) { struct fib6_info *from; From 14f93ce2febce3669d438d1fe99698f1b2be9c53 Mon Sep 17 00:00:00 2001 From: Michael Thalmeier Date: Mon, 22 Dec 2025 15:31:43 +0100 Subject: [PATCH 197/214] net: nfc: nci: Fix parameter validation for packet data Since commit 9c328f54741b ("net: nfc: nci: Add parameter validation for packet data") communication with nci nfc chips is not working any more. The mentioned commit tries to fix access of uninitialized data, but failed to understand that in some cases the data packet is of variable length and can therefore not be compared to the maximum packet length given by the sizeof(struct). Fixes: 9c328f54741b ("net: nfc: nci: Add parameter validation for packet data") Cc: stable@vger.kernel.org Signed-off-by: Michael Thalmeier Signed-off-by: NipaLocal --- net/nfc/nci/ntf.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 418b84e2b2605..0190332cf4546 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -58,7 +58,7 @@ static int nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, struct nci_conn_info *conn_info; int i; - if (skb->len < sizeof(struct nci_core_conn_credit_ntf)) + if (skb->len < offsetofend(struct nci_core_conn_credit_ntf, num_entries)) return -EINVAL; ntf = (struct nci_core_conn_credit_ntf *)skb->data; @@ -68,6 +68,10 @@ static int nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, if (ntf->num_entries > NCI_MAX_NUM_CONN) ntf->num_entries = NCI_MAX_NUM_CONN; + if (skb->len < offsetofend(struct nci_core_conn_credit_ntf, num_entries) + + ntf->num_entries * sizeof(struct conn_credit_entry)) + return -EINVAL; + /* update the credits */ for (i = 0; i < ntf->num_entries; i++) { ntf->conn_entries[i].conn_id = @@ -364,7 +368,7 @@ static int nci_rf_discover_ntf_packet(struct nci_dev *ndev, const __u8 *data; bool add_target = true; - if (skb->len < sizeof(struct nci_rf_discover_ntf)) + if (skb->len < offsetofend(struct nci_rf_discover_ntf, rf_tech_specific_params_len)) return -EINVAL; data = skb->data; @@ -380,6 +384,10 @@ static int nci_rf_discover_ntf_packet(struct nci_dev *ndev, pr_debug("rf_tech_specific_params_len %d\n", ntf.rf_tech_specific_params_len); + if (skb->len < (data - skb->data) + + ntf.rf_tech_specific_params_len + sizeof(ntf.ntf_type)) + return -EINVAL; + if (ntf.rf_tech_specific_params_len > 0) { switch (ntf.rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: @@ -596,7 +604,7 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, const __u8 *data; int err = NCI_STATUS_OK; - if (skb->len < sizeof(struct nci_rf_intf_activated_ntf)) + if (skb->len < offsetofend(struct nci_rf_intf_activated_ntf, rf_tech_specific_params_len)) return -EINVAL; data = skb->data; @@ -628,6 +636,9 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, if (ntf.rf_interface == NCI_RF_INTERFACE_NFCEE_DIRECT) goto listen; + if (skb->len < (data - skb->data) + ntf.rf_tech_specific_params_len) + return -EINVAL; + if (ntf.rf_tech_specific_params_len > 0) { switch (ntf.activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: @@ -668,6 +679,11 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, } } + if (skb->len < (data - skb->data) + sizeof(ntf.data_exch_rf_tech_and_mode) + + sizeof(ntf.data_exch_tx_bit_rate) + sizeof (ntf.data_exch_rx_bit_rate) + + sizeof(ntf.activation_params_len)) + return -EINVAL; + ntf.data_exch_rf_tech_and_mode = *data++; ntf.data_exch_tx_bit_rate = *data++; ntf.data_exch_rx_bit_rate = *data++; @@ -679,6 +695,9 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, pr_debug("data_exch_rx_bit_rate 0x%x\n", ntf.data_exch_rx_bit_rate); pr_debug("activation_params_len %d\n", ntf.activation_params_len); + if (skb->len < (data - skb->data) + ntf.activation_params_len) + return -EINVAL; + if (ntf.activation_params_len > 0) { switch (ntf.rf_interface) { case NCI_RF_INTERFACE_ISO_DEP: From 5aced388a698c04ee6c018bee28cd1f6dcf55a4b Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 22 Dec 2025 06:52:10 -0800 Subject: [PATCH 198/214] netconsole: extract message fragmentation into send_msg_udp() Extract the message fragmentation logic from write_msg() into a dedicated send_msg_udp() function. This improves code readability and prepares for future enhancements. The new send_msg_udp() function handles splitting messages that exceed MAX_PRINT_CHUNK into smaller fragments and sending them sequentially. This function is placed before send_ext_msg_udp() to maintain a logical ordering of related functions. No functional changes - this is purely a refactoring commit. Reviewed-by: Petr Mladek Reviewed-by: Simon Horman Signed-off-by: Breno Leitao Signed-off-by: NipaLocal --- drivers/net/netconsole.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 9cb4dfc242f5f..dc3bd7c9b0498 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -1725,12 +1725,24 @@ static void write_ext_msg(struct console *con, const char *msg, spin_unlock_irqrestore(&target_list_lock, flags); } +static void send_msg_udp(struct netconsole_target *nt, const char *msg, + unsigned int len) +{ + const char *tmp = msg; + int frag, left = len; + + while (left > 0) { + frag = min(left, MAX_PRINT_CHUNK); + send_udp(nt, tmp, frag); + tmp += frag; + left -= frag; + } +} + static void write_msg(struct console *con, const char *msg, unsigned int len) { - int frag, left; unsigned long flags; struct netconsole_target *nt; - const char *tmp; if (oops_only && !oops_in_progress) return; @@ -1747,13 +1759,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len) * at least one target if we die inside here, instead * of unnecessarily keeping all targets in lock-step. */ - tmp = msg; - for (left = len; left;) { - frag = min(left, MAX_PRINT_CHUNK); - send_udp(nt, tmp, frag); - tmp += frag; - left -= frag; - } + send_msg_udp(nt, msg, len); } } spin_unlock_irqrestore(&target_list_lock, flags); From a5c6cb0782f5d56d5f183eb471b2d0c306c9afe3 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 22 Dec 2025 06:52:11 -0800 Subject: [PATCH 199/214] netconsole: convert to NBCON console infrastructure Convert netconsole from the legacy console API to the NBCON framework. NBCON provides threaded printing which unblocks printk()s and flushes in a thread, decoupling network TX from printk() when netconsole is in use. Since netconsole relies on the network stack which cannot safely operate from all atomic contexts, mark both consoles with CON_NBCON_ATOMIC_UNSAFE. (See discussion in [1]) CON_NBCON_ATOMIC_UNSAFE restricts write_atomic() usage to emergency scenarios (panic) where regular messages are sent in threaded mode. Implementation changes: - Unify write_ext_msg() and write_msg() into netconsole_write() - Add device_lock/device_unlock callbacks to manage target_list_lock - Use nbcon_enter_unsafe()/nbcon_exit_unsafe() around network operations - Set write_thread and write_atomic callbacks (both use same function) Link: https://lore.kernel.org/all/b2qps3uywhmjaym4mht2wpxul4yqtuuayeoq4iv4k3zf5wdgh3@tocu6c7mj4lt/ [1] Signed-off-by: Breno Leitao Signed-off-by: NipaLocal --- drivers/net/netconsole.c | 95 ++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index dc3bd7c9b0498..248b401bcaa42 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -1709,22 +1709,6 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, sysdata_len); } -static void write_ext_msg(struct console *con, const char *msg, - unsigned int len) -{ - struct netconsole_target *nt; - unsigned long flags; - - if ((oops_only && !oops_in_progress) || list_empty(&target_list)) - return; - - spin_lock_irqsave(&target_list_lock, flags); - list_for_each_entry(nt, &target_list, list) - if (nt->extended && nt->enabled && netif_running(nt->np.dev)) - send_ext_msg_udp(nt, msg, len); - spin_unlock_irqrestore(&target_list_lock, flags); -} - static void send_msg_udp(struct netconsole_target *nt, const char *msg, unsigned int len) { @@ -1739,29 +1723,60 @@ static void send_msg_udp(struct netconsole_target *nt, const char *msg, } } -static void write_msg(struct console *con, const char *msg, unsigned int len) +/** + * netconsole_write - Generic function to send a msg to all targets + * @wctxt: nbcon write context + * @extended: "true" for extended console mode + * + * Given a nbcon write context, send the message to the netconsole + * targets + */ +static void netconsole_write(struct nbcon_write_context *wctxt, + bool extended) { - unsigned long flags; struct netconsole_target *nt; if (oops_only && !oops_in_progress) return; - /* Avoid taking lock and disabling interrupts unnecessarily */ - if (list_empty(&target_list)) - return; - spin_lock_irqsave(&target_list_lock, flags); list_for_each_entry(nt, &target_list, list) { - if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) { - /* - * We nest this inside the for-each-target loop above - * so that we're able to get as much logging out to - * at least one target if we die inside here, instead - * of unnecessarily keeping all targets in lock-step. - */ - send_msg_udp(nt, msg, len); - } + if (nt->extended != extended || !nt->enabled || + !netif_running(nt->np.dev)) + continue; + + if (!nbcon_enter_unsafe(wctxt)) + continue; + + if (extended) + send_ext_msg_udp(nt, wctxt->outbuf, wctxt->len); + else + send_msg_udp(nt, wctxt->outbuf, wctxt->len); + + nbcon_exit_unsafe(wctxt); } +} + +static void netconsole_write_ext(struct console *con __always_unused, + struct nbcon_write_context *wctxt) +{ + netconsole_write(wctxt, true); +} + +static void netconsole_write_basic(struct console *con __always_unused, + struct nbcon_write_context *wctxt) +{ + netconsole_write(wctxt, false); +} + +static void netconsole_device_lock(struct console *con __always_unused, + unsigned long *flags) +{ + spin_lock_irqsave(&target_list_lock, *flags); +} + +static void netconsole_device_unlock(struct console *con __always_unused, + unsigned long flags) +{ spin_unlock_irqrestore(&target_list_lock, flags); } @@ -1924,15 +1939,21 @@ static void free_param_target(struct netconsole_target *nt) } static struct console netconsole_ext = { - .name = "netcon_ext", - .flags = CON_ENABLED | CON_EXTENDED, - .write = write_ext_msg, + .name = "netcon_ext", + .flags = CON_ENABLED | CON_EXTENDED | CON_NBCON | CON_NBCON_ATOMIC_UNSAFE, + .write_thread = netconsole_write_ext, + .write_atomic = netconsole_write_ext, + .device_lock = netconsole_device_lock, + .device_unlock = netconsole_device_unlock, }; static struct console netconsole = { - .name = "netcon", - .flags = CON_ENABLED, - .write = write_msg, + .name = "netcon", + .flags = CON_ENABLED | CON_NBCON | CON_NBCON_ATOMIC_UNSAFE, + .write_thread = netconsole_write_basic, + .write_atomic = netconsole_write_basic, + .device_lock = netconsole_device_lock, + .device_unlock = netconsole_device_unlock, }; static int __init init_netconsole(void) From 6c15526c008513a3b61a527eef5da739f8ca160e Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 22 Dec 2025 08:08:40 -0800 Subject: [PATCH 200/214] net: stmmac: convert to use .get_rx_ring_count Convert the stmmac driver to use the new .get_rx_ring_count ethtool operation instead of implementing .get_rxnfc for handling ETHTOOL_GRXRINGS command. Since stmmac_get_rxnfc() only handled ETHTOOL_GRXRINGS (returning -EOPNOTSUPP for all other commands), remove it entirely and replace it with the simpler stmmac_get_rx_ring_count() callback. Signed-off-by: Breno Leitao Signed-off-by: NipaLocal --- .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index b155e71aac51f..c1e26965d9b5d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -914,20 +914,11 @@ static int stmmac_set_per_queue_coalesce(struct net_device *dev, u32 queue, return __stmmac_set_coalesce(dev, ec, queue); } -static int stmmac_get_rxnfc(struct net_device *dev, - struct ethtool_rxnfc *rxnfc, u32 *rule_locs) +static u32 stmmac_get_rx_ring_count(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - switch (rxnfc->cmd) { - case ETHTOOL_GRXRINGS: - rxnfc->data = priv->plat->rx_queues_to_use; - break; - default: - return -EOPNOTSUPP; - } - - return 0; + return priv->plat->rx_queues_to_use; } static u32 stmmac_get_rxfh_key_size(struct net_device *dev) @@ -1121,7 +1112,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .get_eee = stmmac_ethtool_op_get_eee, .set_eee = stmmac_ethtool_op_set_eee, .get_sset_count = stmmac_get_sset_count, - .get_rxnfc = stmmac_get_rxnfc, + .get_rx_ring_count = stmmac_get_rx_ring_count, .get_rxfh_key_size = stmmac_get_rxfh_key_size, .get_rxfh_indir_size = stmmac_get_rxfh_indir_size, .get_rxfh = stmmac_get_rxfh, From 10591719129883005df11111250a7c5135dbf01d Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 22 Dec 2025 09:16:27 -0800 Subject: [PATCH 201/214] net: gve: convert to use .get_rx_ring_count Convert the Google Virtual Ethernet (GVE) driver to use the new .get_rx_ring_count ethtool operation instead of handling ETHTOOL_GRXRINGS in .get_rxnfc. This simplifies the code by moving the ring count query to a dedicated callback. The new callback provides the same functionality in a more direct way, following the ongoing ethtool API modernization. Signed-off-by: Breno Leitao Signed-off-by: NipaLocal --- drivers/net/ethernet/google/gve/gve_ethtool.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 52500ae8348e6..9ed1d45294278 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -815,15 +815,19 @@ static int gve_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) return err; } +static u32 gve_get_rx_ring_count(struct net_device *netdev) +{ + struct gve_priv *priv = netdev_priv(netdev); + + return priv->rx_cfg.num_queues; +} + static int gve_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { struct gve_priv *priv = netdev_priv(netdev); int err = 0; switch (cmd->cmd) { - case ETHTOOL_GRXRINGS: - cmd->data = priv->rx_cfg.num_queues; - break; case ETHTOOL_GRXCLSRLCNT: if (!priv->max_flow_rules) return -EOPNOTSUPP; @@ -966,6 +970,7 @@ const struct ethtool_ops gve_ethtool_ops = { .get_channels = gve_get_channels, .set_rxnfc = gve_set_rxnfc, .get_rxnfc = gve_get_rxnfc, + .get_rx_ring_count = gve_get_rx_ring_count, .get_rxfh_indir_size = gve_get_rxfh_indir_size, .get_rxfh_key_size = gve_get_rxfh_key_size, .get_rxfh = gve_get_rxfh, From c02233249331d61ce342e2a72265730ba9a937be Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 11 May 2025 19:46:34 -0700 Subject: [PATCH 202/214] nipa: disable random kunit tests Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- drivers/clk/Kconfig | 4 ---- drivers/firewire/Kconfig | 5 ----- drivers/firmware/cirrus/Kconfig | 1 - drivers/fpga/tests/Kconfig | 1 - drivers/gpu/drm/vc4/Kconfig | 1 - drivers/gpu/drm/xe/Kconfig.debug | 1 - drivers/hid/Kconfig | 1 - fs/ext4/Kconfig | 1 - fs/fat/Kconfig | 1 - sound/soc/codecs/Kconfig | 1 - tools/testing/kunit/configs/all_tests.config | 16 ---------------- 11 files changed, 33 deletions(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3a1611008e48e..7fd4192302c94 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -555,7 +555,6 @@ source "drivers/clk/zynqmp/Kconfig" config CLK_KUNIT_TEST tristate "Basic Clock Framework Kunit Tests" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS select DTC help Kunit tests for the common clock framework. @@ -563,7 +562,6 @@ config CLK_KUNIT_TEST config CLK_FIXED_RATE_KUNIT_TEST tristate "Basic fixed rate clk type KUnit test" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS select DTC help KUnit tests for the basic fixed rate clk type. @@ -572,14 +570,12 @@ config CLK_GATE_KUNIT_TEST tristate "Basic gate type Kunit test" if !KUNIT_ALL_TESTS depends on KUNIT depends on !S390 - default KUNIT_ALL_TESTS help Kunit test for the basic clk gate type. config CLK_FD_KUNIT_TEST tristate "Basic fractional divider type Kunit test" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS help Kunit test for the clk-fractional-divider type. diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index a5f5e250223a1..8cddc84d4d236 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -21,7 +21,6 @@ config FIREWIRE config FIREWIRE_KUNIT_UAPI_TEST tristate "KUnit tests for layout of structure in UAPI" if !KUNIT_ALL_TESTS depends on FIREWIRE && KUNIT - default KUNIT_ALL_TESTS help This builds the KUnit tests whether structures exposed to user space have expected layout. @@ -37,7 +36,6 @@ config FIREWIRE_KUNIT_UAPI_TEST config FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST tristate "KUnit tests for device attributes" if !KUNIT_ALL_TESTS depends on FIREWIRE && KUNIT - default KUNIT_ALL_TESTS help This builds the KUnit tests for device attribute for node and unit. @@ -53,7 +51,6 @@ config FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST config FIREWIRE_KUNIT_PACKET_SERDES_TEST tristate "KUnit tests for packet serialization/deserialization" if !KUNIT_ALL_TESTS depends on FIREWIRE && KUNIT - default KUNIT_ALL_TESTS help This builds the KUnit tests for packet serialization and deserialization. @@ -69,7 +66,6 @@ config FIREWIRE_KUNIT_PACKET_SERDES_TEST config FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST tristate "KUnit tests for helpers of self ID sequence" if !KUNIT_ALL_TESTS depends on FIREWIRE && KUNIT - default KUNIT_ALL_TESTS help This builds the KUnit tests for helpers of self ID sequence. @@ -95,7 +91,6 @@ config FIREWIRE_OHCI config FIREWIRE_KUNIT_OHCI_SERDES_TEST tristate "KUnit tests for serialization/deserialization of data in buffers/registers" if !KUNIT_ALL_TESTS depends on FIREWIRE && KUNIT - default KUNIT_ALL_TESTS help This builds the KUnit tests to check serialization and deserialization of data in buffers and registers defined in 1394 OHCI specification. diff --git a/drivers/firmware/cirrus/Kconfig b/drivers/firmware/cirrus/Kconfig index e3c2e38b746df..ad7055f7e48d7 100644 --- a/drivers/firmware/cirrus/Kconfig +++ b/drivers/firmware/cirrus/Kconfig @@ -10,7 +10,6 @@ config FW_CS_DSP_KUNIT_TEST_UTILS config FW_CS_DSP_KUNIT_TEST tristate "KUnit tests for Cirrus Logic cs_dsp" if !KUNIT_ALL_TESTS depends on KUNIT && REGMAP && FW_CS_DSP - default KUNIT_ALL_TESTS select FW_CS_DSP_KUNIT_TEST_UTILS help This builds KUnit tests for cs_dsp. diff --git a/drivers/fpga/tests/Kconfig b/drivers/fpga/tests/Kconfig index e4a64815f16d6..e8ded96191df9 100644 --- a/drivers/fpga/tests/Kconfig +++ b/drivers/fpga/tests/Kconfig @@ -1,7 +1,6 @@ config FPGA_KUNIT_TESTS tristate "KUnit test for the FPGA subsystem" if !KUNIT_ALL_TESTS depends on FPGA && FPGA_REGION && FPGA_BRIDGE && KUNIT=y - default KUNIT_ALL_TESTS help This builds unit tests for the FPGA subsystem diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index bb8c40be32503..5ba1e7f7e7c38 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -44,7 +44,6 @@ config DRM_VC4_KUNIT_TEST tristate "KUnit tests for VC4" if !KUNIT_ALL_TESTS depends on DRM_VC4 && KUNIT select DRM_KUNIT_TEST_HELPERS - default KUNIT_ALL_TESTS help This builds unit tests for the VC4 DRM/KMS driver. This option is not useful for distributions or general kernels, but only for kernel diff --git a/drivers/gpu/drm/xe/Kconfig.debug b/drivers/gpu/drm/xe/Kconfig.debug index 01227c77f6d70..886657fa1c2a9 100644 --- a/drivers/gpu/drm/xe/Kconfig.debug +++ b/drivers/gpu/drm/xe/Kconfig.debug @@ -76,7 +76,6 @@ config DRM_XE_DEBUG_MEM config DRM_XE_KUNIT_TEST tristate "KUnit tests for the drm xe driver" if !KUNIT_ALL_TESTS depends on DRM_XE && KUNIT && DEBUG_FS - default KUNIT_ALL_TESTS select DRM_EXPORT_FOR_TESTS if m help Choose this option to allow the driver to perform selftests under diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 920a64b66b25b..140d15a701365 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1416,7 +1416,6 @@ config HID_KUNIT_TEST depends on KUNIT depends on HID_BATTERY_STRENGTH depends on HID_UCLOGIC - default KUNIT_ALL_TESTS help This builds unit tests for HID. This option is not useful for distributions or general kernels, but only for kernel diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 01873c2a34ad2..5bad43e85ee1a 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -77,7 +77,6 @@ config EXT4_DEBUG config EXT4_KUNIT_TESTS tristate "KUnit tests for ext4" if !KUNIT_ALL_TESTS depends on EXT4_FS && KUNIT - default KUNIT_ALL_TESTS help This builds the ext4 KUnit tests. diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 25fae1c83725b..79c8502ba9115 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -121,7 +121,6 @@ config FAT_DEFAULT_UTF8 config FAT_KUNIT_TEST tristate "Unit Tests for FAT filesystems" if !KUNIT_ALL_TESTS depends on KUNIT && FAT_FS - default KUNIT_ALL_TESTS help This builds the FAT KUnit tests diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061791e619074..1a3a60a493fd8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -788,7 +788,6 @@ config SND_SOC_CS_AMP_LIB config SND_SOC_CS_AMP_LIB_TEST tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS depends on SND_SOC_CS_AMP_LIB && KUNIT - default KUNIT_ALL_TESTS help This builds KUnit tests for the Cirrus Logic common amplifier library. diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config index 422e186cf3cf1..2f093048d985c 100644 --- a/tools/testing/kunit/configs/all_tests.config +++ b/tools/testing/kunit/configs/all_tests.config @@ -36,22 +36,6 @@ CONFIG_MAC80211=y CONFIG_WLAN_VENDOR_INTEL=y CONFIG_IWLWIFI=y -CONFIG_DAMON=y -CONFIG_DAMON_VADDR=y -CONFIG_DAMON_PADDR=y - CONFIG_REGMAP_BUILD=y -CONFIG_AUDIT=y - CONFIG_PRIME_NUMBERS=y - -CONFIG_SECURITY=y -CONFIG_SECURITY_APPARMOR=y -CONFIG_SECURITY_LANDLOCK=y - -CONFIG_SOUND=y -CONFIG_SND=y -CONFIG_SND_SOC=y -CONFIG_SND_SOC_TOPOLOGY_BUILD=y -CONFIG_SND_SOC_CS35L56_I2C=y From 3b83db6311ccd55c37603fc5e050313243e3e855 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 6 Aug 2025 16:39:42 -0700 Subject: [PATCH 203/214] nipa: config: disable CPU_MITIGATIONS These are unlikely to matter for CI testing and they slow things down. Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- arch/x86/configs/x86_64_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 7d7310cdf8b0a..8af4621961ccf 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -29,6 +29,7 @@ CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_KEXEC=y CONFIG_SMP=y +CONFIG_CPU_MITIGATIONS=n CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y From f082a26a17b4da94a0ad883ea6cf429dda1dd406 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 17 Aug 2024 10:53:12 -0700 Subject: [PATCH 204/214] nipa: profile (time) test output Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- tools/testing/selftests/kselftest/prefix.pl | 8 ++++++++ tools/testing/selftests/kselftest/runner.sh | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kselftest/prefix.pl b/tools/testing/selftests/kselftest/prefix.pl index 12a7f4ca2684d..b8e4d697e3b3a 100755 --- a/tools/testing/selftests/kselftest/prefix.pl +++ b/tools/testing/selftests/kselftest/prefix.pl @@ -4,12 +4,15 @@ # to have unbuffering forced with "stdbuf -i0 -o0 -e0 $cmd". use strict; use IO::Handle; +use Time::HiRes qw( time ); binmode STDIN; binmode STDOUT; STDOUT->autoflush(1); +my $start_time = time(); +my $prev_time = $start_time; my $needed = 1; while (1) { my $char; @@ -17,6 +20,11 @@ exit 0 if ($bytes == 0); if ($needed) { print "# "; + if ($ENV{kselftest_profile}) { + my $now = time(); + printf("%.2f [+%.2f] ", $now - $start_time, $now - $prev_time); + $prev_time = $now; + } $needed = 0; } print $char; diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 3a62039fa6217..dfae04819075d 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -95,7 +95,7 @@ run_one() fi field=$(echo "$line" | cut -d= -f1) value=$(echo "$line" | cut -d= -f2-) - eval "kselftest_$field"="$value" + eval "export kselftest_$field"="$value" done < "$settings" fi From d942e14dc1310b69c5dc22ffc39c49165b874618 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 30 Jul 2025 07:19:58 -0700 Subject: [PATCH 205/214] nipa: config: x86: use periodic HZ tick Let's see if this increases stability of timing-related results.. Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- arch/x86/configs/i386_defconfig | 2 +- arch/x86/configs/x86_64_defconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index 79fa38ca954d1..16a3de1c1b9a7 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -2,7 +2,7 @@ CONFIG_WERROR=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y -CONFIG_NO_HZ=y +CONFIG_HZ_PERIODIC=y CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT_VOLUNTARY=y CONFIG_BSD_PROCESS_ACCT=y diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 8af4621961ccf..28d6541d1857f 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -2,7 +2,7 @@ CONFIG_WERROR=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y -CONFIG_NO_HZ=y +CONFIG_HZ_PERIODIC=y CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT_VOLUNTARY=y CONFIG_BSD_PROCESS_ACCT=y From fe5e07e4177e3704026efaeebe95f223dfe519cf Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 13 Jun 2025 15:27:15 -0700 Subject: [PATCH 206/214] nipa: timestamp - try waking Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- tools/testing/selftests/net/txtimestamp.c | 33 ++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/txtimestamp.c b/tools/testing/selftests/net/txtimestamp.c index bcc14688661dc..4b4bbc2ce5c9b 100644 --- a/tools/testing/selftests/net/txtimestamp.c +++ b/tools/testing/selftests/net/txtimestamp.c @@ -62,6 +62,7 @@ static int do_ipv4 = 1; static int do_ipv6 = 1; static int cfg_payload_len = 10; static int cfg_poll_timeout = 100; +static bool cfg_wake_every_msec = true; static int cfg_delay_snd; static int cfg_delay_ack; static int cfg_delay_tolerance_usec = 500; @@ -286,6 +287,16 @@ static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); } +static int64_t get_time_now_us64(void) +{ + static struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts)) + error(1, errno, "clock_gettime"); + + return timespec_to_us64(&ts); +} + static void __epoll(int epfd) { struct epoll_event events; @@ -300,11 +311,19 @@ static void __epoll(int epfd) static void __poll(int fd) { struct pollfd pollfd; + int64_t end_of_wait; + int timeout; int ret; - memset(&pollfd, 0, sizeof(pollfd)); - pollfd.fd = fd; - ret = poll(&pollfd, 1, cfg_poll_timeout); + timeout = cfg_wake_every_msec ? 1 : cfg_poll_timeout; + end_of_wait = get_time_now_us64() + cfg_poll_timeout * 1000; + + do { + memset(&pollfd, 0, sizeof(pollfd)); + pollfd.fd = fd; + ret = poll(&pollfd, 1, timeout); + } while (!ret && get_time_now_us64() < end_of_wait); + if (ret != 1) error(1, errno, "poll"); } @@ -707,6 +726,7 @@ static void __attribute__((noreturn)) usage(const char *filepath) " -P: use PF_PACKET\n" " -r: use raw\n" " -R: use raw (IP_HDRINCL)\n" + " -s: single sleep until timeout (from -S), by default wake every 1msec\n" " -S N: usec to sleep before reading error queue\n" " -t N: tolerance (usec) for timestamp validation\n" " -u: use udp\n" @@ -723,7 +743,7 @@ static void parse_opt(int argc, char **argv) int c; while ((c = getopt(argc, argv, - "46bc:CeEFhIl:LnNo:p:PrRS:t:uv:V:x")) != -1) { + "46bc:CeEFhIl:LnNo:p:PrRsS:t:uv:V:x")) != -1) { switch (c) { case '4': do_ipv6 = 0; @@ -787,6 +807,9 @@ static void parse_opt(int argc, char **argv) cfg_proto = SOCK_RAW; cfg_ipproto = IPPROTO_RAW; break; + case 's': /* sleep 'till timeout */ + cfg_wake_every_msec = false; + break; case 'S': cfg_sleep_usec = strtoul(optarg, NULL, 10); break; @@ -825,6 +848,8 @@ static void parse_opt(int argc, char **argv) error(1, 0, "cannot ask for pktinfo over pf_packet"); if (cfg_busy_poll && cfg_use_epoll) error(1, 0, "pass epoll or busy_poll, not both"); + if (cfg_wake_every_msec && cfg_use_epoll) + error(1, 0, "periodic wake not implemented for epoll, use -s"); if (cfg_proto == SOCK_STREAM && cfg_use_cmsg_opt_id) error(1, 0, "TCP sockets don't support SCM_TS_OPT_ID"); From a66bd22449c194836aa8ec9c47cbf7f48005ad16 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 30 May 2025 14:23:23 -0700 Subject: [PATCH 207/214] nipa: fbnic: link up on QEMU Alex will send phylink patches soon which will make us link up on QEMU again, but for now let's hack up the link. Gives us a chance to add another QEMU NIC test to "HW" runners in the CI. Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index 9240673c7533d..4a44d5261f009 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -218,6 +218,9 @@ static void fbnic_service_task(struct work_struct *work) fbnic_fw_check_heartbeat(fbd); + if (!netif_carrier_ok(netdev)) + netif_carrier_on(fbd->netdev); + fbnic_health_check(fbd); fbnic_bmc_rpc_check(fbd); From 8d7042ed168773461be0b5432d13ab18d884acd3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 3 Nov 2024 16:10:42 -0800 Subject: [PATCH 208/214] nipa: selftests: net: enable profiling Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- tools/testing/selftests/net/settings | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/settings b/tools/testing/selftests/net/settings index ed8418e8217a0..a38764182822e 100644 --- a/tools/testing/selftests/net/settings +++ b/tools/testing/selftests/net/settings @@ -1 +1,2 @@ timeout=3600 +profile=1 From 24033ebff19fe2e126a80c502ba87ff8f335f5a7 Mon Sep 17 00:00:00 2001 From: fedora Cloud User Date: Thu, 1 Feb 2024 06:34:18 -0800 Subject: [PATCH 209/214] nipa: forwarding: set timeout to 3 hours tc_actions.sh keeps hanging the forwarding tests. sdf@: tdc & tdc-dbg started intermittenly failing around Sep 25th Signed-off-by: NipaLocal --- tools/testing/selftests/net/forwarding/settings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/forwarding/settings b/tools/testing/selftests/net/forwarding/settings index e7b9417537fbc..ff3de4936a009 100644 --- a/tools/testing/selftests/net/forwarding/settings +++ b/tools/testing/selftests/net/forwarding/settings @@ -1 +1,2 @@ -timeout=0 +timeout=10800 +profile=1 From 99cdbb67fd8972fd67a32183a8e3df81fc16ae3f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 30 Jul 2025 07:17:43 -0700 Subject: [PATCH 210/214] nipa: config: x86: disable GPUs and sound We exclusively use headless VMs today, don't waste time compiling sound and GPU drivers. Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- arch/x86/configs/i386_defconfig | 13 ------------- arch/x86/configs/x86_64_defconfig | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index 16a3de1c1b9a7..652896207f2f0 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -190,19 +190,6 @@ CONFIG_HPET=y # CONFIG_HPET_MMAP is not set CONFIG_I2C_I801=y CONFIG_WATCHDOG=y -CONFIG_AGP=y -CONFIG_AGP_AMD64=y -CONFIG_AGP_INTEL=y -CONFIG_DRM=y -CONFIG_DRM_I915=y -CONFIG_DRM_VIRTIO_GPU=y -CONFIG_SOUND=y -CONFIG_SND=y -CONFIG_SND_HRTIMER=y -CONFIG_SND_SEQUENCER=y -CONFIG_SND_SEQ_DUMMY=y -CONFIG_SND_HDA_INTEL=y -CONFIG_SND_HDA_HWDEP=y CONFIG_HIDRAW=y CONFIG_HID_GYRATION=y CONFIG_HID_NTRIG=y diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index 28d6541d1857f..d9e4feeb5a3d5 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -188,19 +188,6 @@ CONFIG_HPET=y # CONFIG_HPET_MMAP is not set CONFIG_I2C_I801=y CONFIG_WATCHDOG=y -CONFIG_AGP=y -CONFIG_AGP_AMD64=y -CONFIG_AGP_INTEL=y -CONFIG_DRM=y -CONFIG_DRM_I915=y -CONFIG_DRM_VIRTIO_GPU=y -CONFIG_SOUND=y -CONFIG_SND=y -CONFIG_SND_HRTIMER=y -CONFIG_SND_SEQUENCER=y -CONFIG_SND_SEQ_DUMMY=y -CONFIG_SND_HDA_INTEL=y -CONFIG_SND_HDA_HWDEP=y CONFIG_HIDRAW=y CONFIG_HID_GYRATION=y CONFIG_HID_NTRIG=y From 24c301ba5eb399afc00e548c13081af9dd391ad6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 30 Jul 2025 16:44:59 -0700 Subject: [PATCH 211/214] nipa: disable 6.17's merge window kunit tests Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- kernel/irq/Kconfig | 1 - lib/crypto/tests/Kconfig | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 1b4254d19a73e..c49dee35fc520 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -144,7 +144,6 @@ config IRQ_KUNIT_TEST bool "KUnit tests for IRQ management APIs" if !KUNIT_ALL_TESTS depends on KUNIT=y depends on SPARSE_IRQ - default KUNIT_ALL_TESTS select IRQ_DOMAIN imply SMP help diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig index 61d435c450bb4..be06bd634b3f8 100644 --- a/lib/crypto/tests/Kconfig +++ b/lib/crypto/tests/Kconfig @@ -3,7 +3,6 @@ config CRYPTO_LIB_BLAKE2B_KUNIT_TEST tristate "KUnit tests for BLAKE2b" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_BLAKE2B help @@ -12,7 +11,6 @@ config CRYPTO_LIB_BLAKE2B_KUNIT_TEST config CRYPTO_LIB_BLAKE2S_KUNIT_TEST tristate "KUnit tests for BLAKE2s" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE # No need to select CRYPTO_LIB_BLAKE2S here, as that option doesn't # exist; the BLAKE2s code is always built-in for the /dev/random driver. @@ -22,7 +20,6 @@ config CRYPTO_LIB_BLAKE2S_KUNIT_TEST config CRYPTO_LIB_CURVE25519_KUNIT_TEST tristate "KUnit tests for Curve25519" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_CURVE25519 help @@ -31,7 +28,6 @@ config CRYPTO_LIB_CURVE25519_KUNIT_TEST config CRYPTO_LIB_MD5_KUNIT_TEST tristate "KUnit tests for MD5" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_MD5 help @@ -41,7 +37,6 @@ config CRYPTO_LIB_MD5_KUNIT_TEST config CRYPTO_LIB_POLY1305_KUNIT_TEST tristate "KUnit tests for Poly1305" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_POLY1305 help @@ -50,7 +45,6 @@ config CRYPTO_LIB_POLY1305_KUNIT_TEST config CRYPTO_LIB_POLYVAL_KUNIT_TEST tristate "KUnit tests for POLYVAL" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_POLYVAL help @@ -59,7 +53,6 @@ config CRYPTO_LIB_POLYVAL_KUNIT_TEST config CRYPTO_LIB_SHA1_KUNIT_TEST tristate "KUnit tests for SHA-1" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_SHA1 help @@ -71,7 +64,6 @@ config CRYPTO_LIB_SHA1_KUNIT_TEST config CRYPTO_LIB_SHA256_KUNIT_TEST tristate "KUnit tests for SHA-224 and SHA-256" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_SHA256 help @@ -83,7 +75,6 @@ config CRYPTO_LIB_SHA256_KUNIT_TEST config CRYPTO_LIB_SHA512_KUNIT_TEST tristate "KUnit tests for SHA-384 and SHA-512" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_SHA512 help @@ -93,7 +84,6 @@ config CRYPTO_LIB_SHA512_KUNIT_TEST config CRYPTO_LIB_SHA3_KUNIT_TEST tristate "KUnit tests for SHA-3" if !KUNIT_ALL_TESTS depends on KUNIT - default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS select CRYPTO_LIB_BENCHMARK_VISIBLE select CRYPTO_LIB_SHA3 help From 33d167e9dbadc65a6940f36e6949667cab8d748e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 29 Aug 2024 19:57:39 -0700 Subject: [PATCH 212/214] nipa: tc_action dbg Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- .../selftests/net/forwarding/tc_actions.sh | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/forwarding/tc_actions.sh b/tools/testing/selftests/net/forwarding/tc_actions.sh index ea89e558672db..5823d94b19e9b 100755 --- a/tools/testing/selftests/net/forwarding/tc_actions.sh +++ b/tools/testing/selftests/net/forwarding/tc_actions.sh @@ -223,19 +223,32 @@ mirred_egress_to_ingress_tcp_test() ip_proto icmp \ action drop - ip vrf exec v$h1 ncat --recv-only -w10 -l -p 12345 -o $mirred_e2i_tf2 & - local rpid=$! - ip vrf exec v$h1 ncat -w1 --send-only 192.0.2.2 12345 <$mirred_e2i_tf1 - wait -n $rpid - cmp -s $mirred_e2i_tf1 $mirred_e2i_tf2 - check_err $? "server output check failed" + echo P2 $MZ $h1 -c 10 -p 64 -a $h1mac -b $h1mac -A 192.0.2.1 -B 192.0.2.1 \ -t icmp "ping,id=42,seq=5" -q + echo P2.1 tc_check_packets "dev $h1 egress" 101 10 check_err $? "didn't mirred redirect ICMP" + echo P2.2 tc_check_packets "dev $h1 ingress" 102 10 check_err $? "didn't drop mirred ICMP" + echo P2.3 + + echo P1 + + ip vrf exec v$h1 ncat --recv-only -w10 -l -p 12345 -o $mirred_e2i_tf2 & + local rpid=$! + sleep 0.2 + echo P1.1 + ip vrf exec v$h1 ncat -w1 --send-only 192.0.2.2 12345 <$mirred_e2i_tf1 + echo P1.2 + sleep 0.2 + wait -n $rpid + cmp -s $mirred_e2i_tf1 $mirred_e2i_tf2 + check_err $? "server output check failed" + + echo P3 tc filter del dev $h1 egress protocol ip pref 100 handle 100 flower tc filter del dev $h1 egress protocol ip pref 101 handle 101 flower @@ -359,4 +372,5 @@ else tests_run fi +dmesg exit $EXIT_STATUS From cc2ff75b6bc6362d60747e73322a974e47066c61 Mon Sep 17 00:00:00 2001 From: NipaLocal Date: Tue, 11 Mar 2025 22:49:47 -0700 Subject: [PATCH 213/214] nipa: drv: net: add timeout Signed-off-by: NipaLocal --- tools/testing/selftests/drivers/net/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/drivers/net/settings diff --git a/tools/testing/selftests/drivers/net/settings b/tools/testing/selftests/drivers/net/settings new file mode 100644 index 0000000000000..a953c96aa16e1 --- /dev/null +++ b/tools/testing/selftests/drivers/net/settings @@ -0,0 +1 @@ +timeout=180 From b2b4174b8d5510fbeda9c4b41647583ad28747e2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 24 Jul 2025 06:47:48 -0700 Subject: [PATCH 214/214] nipa: config: disable kmemleak auto scan kmemleak auto scan could be a source of latency for the tests. We run a full scan after the tests manually, we don't need the autoscan thread to be enabled. Signed-off-by: Jakub Kicinski Signed-off-by: NipaLocal --- kernel/configs/debug.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/configs/debug.config b/kernel/configs/debug.config index 9f6ab7dabf672..de2c0ede59c45 100644 --- a/kernel/configs/debug.config +++ b/kernel/configs/debug.config @@ -58,7 +58,7 @@ CONFIG_DEBUG_NET=y CONFIG_PAGE_EXTENSION=y CONFIG_PAGE_OWNER=y CONFIG_DEBUG_KMEMLEAK=y -CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=y +CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=n CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 CONFIG_DEBUG_OBJECTS_FREE=y