From: Sasha Levin Date: Thu, 28 May 2026 15:20:29 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v5.10.258~17 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=3fd8927b2d63b2430202cff0b65750859b33e156;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/arm-integrator-fix-early-initialization.patch b/queue-5.10/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..432529f93d --- /dev/null +++ b/queue-5.10/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From 78b6e2f61b0fc23c98d504b29c9b77701ee4782b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-integrator/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c +index b7eb4038798b6..b6d54cee5b792 100644 +--- a/arch/arm/mach-integrator/integrator_cp.c ++++ b/arch/arm/mach-integrator/integrator_cp.c +@@ -88,14 +88,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -121,6 +113,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -140,7 +136,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-5.10/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-5.10/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..ae3396848b --- /dev/null +++ b/queue-5.10/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From a73ae9047870e4b30ba2d371b33d1763677d1b9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-5.10/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-5.10/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..6aef4c72a1 --- /dev/null +++ b/queue-5.10/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From e0c31cb2fe30aa89af84d557e8983fcfe0f47b16 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index e1bdcd345328b..a4446d7baa390 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -925,6 +925,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -932,9 +933,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-5.10/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch b/queue-5.10/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch new file mode 100644 index 0000000000..b11cdb187e --- /dev/null +++ b/queue-5.10/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch @@ -0,0 +1,69 @@ +From b6dca7311a781bce78e791bad90dcf88da6b6a57 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Nov 2024 22:16:15 +0200 +Subject: gpiolib: cdev: use !mem_is_zero() instead of memchr_inv(s, 0, n) + +From: Andy Shevchenko + +[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ] + +Use the mem_is_zero() helper where possible. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 3b0292c244eb2..e1bdcd345328b 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -14,13 +14,13 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -932,7 +932,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + +- if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) ++ if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + + for (i = 0; i < num_lines; i++) { +@@ -1324,7 +1324,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) + return -EINVAL; + +- if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) ++ if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) + return -EINVAL; + + lc = &ulr.config; +@@ -2069,7 +2069,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +- if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) ++ if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) + return -EINVAL; + + desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); +-- +2.53.0 + diff --git a/queue-5.10/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-5.10/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..e05b793e1b --- /dev/null +++ b/queue-5.10/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 474270bddda7ccf685f0369d1f5abc70345c2374 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 84a9c9e761bcd..3a7b231759098 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -222,7 +222,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-5.10/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-5.10/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..58d3c361db --- /dev/null +++ b/queue-5.10/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From 7e561bb36a55ed2dd462ecf104c5839de13472dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index 1e8f71ffc8ce7..7fff700eab2b1 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -438,14 +438,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + enum ice_status ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-5.10/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch b/queue-5.10/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch new file mode 100644 index 0000000000..f198e92d51 --- /dev/null +++ b/queue-5.10/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch @@ -0,0 +1,57 @@ +From a13cdc97327de0502ef6b15e31823c952746bc3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 23:03:28 -0400 +Subject: ipv6: route: Unregister netdevice notifier on BPF init failure + +From: Yuho Choi + +[ Upstream commit 1341db322417266fb5845df81d28305b83a37324 ] + +ip6_route_init() registers ip6_route_dev_notifier before registering the +IPv6 route BPF iterator target. If bpf_iter_register() fails after the +notifier has been registered, the error path currently jumps to +out_register_late_subsys and unwinds the RTNL handlers and pernet route +state without removing the notifier from the netdevice notifier chain. + +This leaves ip6_route_dev_notify() callable after the IPv6 route state it +uses has been torn down. Add a separate unwind label for the BPF iterator +failure path and unregister the netdevice notifier before continuing with +the existing cleanup. + +Fixes: 138d0be35b14 ("net: bpf: Add netlink and ipv6_route bpf_iter targets") +Signed-off-by: Yuho Choi +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 27736b5847378..f2b80dedd8e0f 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6579,7 +6579,7 @@ int __init ip6_route_init(void) + #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) + ret = bpf_iter_register(); + if (ret) +- goto out_register_late_subsys; ++ goto out_register_notifier; + #endif + #endif + +@@ -6593,6 +6593,10 @@ int __init ip6_route_init(void) + out: + return ret; + ++#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) ++out_register_notifier: ++ unregister_netdevice_notifier(&ip6_route_dev_notifier); ++#endif + out_register_late_subsys: + rtnl_unregister_all(PF_INET6); + unregister_pernet_subsys(&ip6_route_net_late_ops); +-- +2.53.0 + diff --git a/queue-5.10/irqchip-ath79-cpu-remove-unused-function.patch b/queue-5.10/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..52c9b287c5 --- /dev/null +++ b/queue-5.10/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From b360fa5d41cb8b8c665a27fad5deade506ae69a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-5.10/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-5.10/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..81fd46ee0b --- /dev/null +++ b/queue-5.10/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From 022d6439e9c6e1244f0590f22d9a23a9b43237a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 00909e6a24438..9eb78ea5e90fc 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -15,8 +15,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-5.10/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-5.10/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..26b709a3b6 --- /dev/null +++ b/queue-5.10/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From 4b3d4042bd67d4d07e68e09f0f8ee43034214e17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 9eb78ea5e90fc..48d4a2d95fd80 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,6 +16,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-5.10/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-5.10/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..ccbd59f99a --- /dev/null +++ b/queue-5.10/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From f616728494cacce73faef93c2300c7011f5bd2e5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index 67409a53d5100..a406e8ac2f612 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1922,6 +1922,9 @@ static int ag71xx_probe(struct platform_device *pdev) + return -ENOMEM; + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-5.10/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-5.10/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..e3381114ab --- /dev/null +++ b/queue-5.10/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 88df69222154a515e57b4389ab41dc7ac6551487 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 642ef6b3eebaf..3e93d1115f1aa 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1414,6 +1415,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1427,7 +1429,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1467,6 +1468,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1477,6 +1479,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1511,6 +1514,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1519,6 +1523,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1528,6 +1533,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1857,6 +1863,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-5.10/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-5.10/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..039b52b241 --- /dev/null +++ b/queue-5.10/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From 02b46ed4b4516ea0928f2570d9c4e41711e35c94 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index ee51367df6488..642ef6b3eebaf 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1463,6 +1463,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-5.10/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-5.10/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..949dabc37d --- /dev/null +++ b/queue-5.10/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From 9ff241312a9ebe43012a897266afb97791f952c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 04a034cd5183f..ee51367df6488 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -121,6 +121,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1412,10 +1414,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1469,6 +1471,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1519,6 +1522,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1847,6 +1851,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-5.10/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-5.10/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..38fb87e0cd --- /dev/null +++ b/queue-5.10/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From eeed9b7983f1f3b5d11b63fe4f16b5ab16664d96 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index 33ace33070593..77af8ace4d7b1 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1270,7 +1270,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1297,7 +1296,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-5.10/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-5.10/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..3d23839689 --- /dev/null +++ b/queue-5.10/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From b376d2b50e3e4ae3461c0357bcdb461d85b6743e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index a300d1ac13a88..9969222dd2150 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -741,11 +741,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-5.10/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-5.10/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..85e3b38871 --- /dev/null +++ b/queue-5.10/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From 59444baf844b89964cf2e0d0093049a04d173364 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 9969222dd2150..1b8e003d5e70b 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -730,21 +730,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-5.10/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-5.10/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..6b21ef7177 --- /dev/null +++ b/queue-5.10/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From 98259646c4b48ab42ad83a32a878317256321570 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 8834436bc9dbc..e3a9278c06842 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-5.10/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-5.10/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..a754b69162 --- /dev/null +++ b/queue-5.10/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From e11997a50b3262d6436ca7302a6ba7af6bc11963 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel-hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c +index 12d695adf3f74..f52367363d530 100644 +--- a/drivers/platform/x86/intel-hid.c ++++ b/drivers/platform/x86/intel-hid.c +@@ -471,12 +471,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index 7b00f45e7f..fdc6e13463 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -573,3 +573,24 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +arm-integrator-fix-early-initialization.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +irqchip-ath79-cpu-remove-unused-function.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +net-ag71xx-check-error-for-platform_get_irq.patch +string-add-mem_is_zero-helper-to-check-if-memory-are.patch +gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch diff --git a/queue-5.10/string-add-mem_is_zero-helper-to-check-if-memory-are.patch b/queue-5.10/string-add-mem_is_zero-helper-to-check-if-memory-are.patch new file mode 100644 index 0000000000..9e2f2119ce --- /dev/null +++ b/queue-5.10/string-add-mem_is_zero-helper-to-check-if-memory-are.patch @@ -0,0 +1,51 @@ +From 864a93a86dad7d78c0dec81bea706593597bfff4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Aug 2024 13:00:34 +0300 +Subject: string: add mem_is_zero() helper to check if memory area is all zeros + +From: Jani Nikula + +[ Upstream commit 3942bb49728ad9e1f94d953a88af169a8f5d8099 ] + +Almost two thirds of the memchr_inv() usages check if the memory area is +all zeros, with no interest in where in the buffer the first non-zero +byte is located. Checking for !memchr_inv(s, 0, n) is also not very +intuitive or discoverable. Add an explicit mem_is_zero() helper for this +use case. + +Reviewed-by: Kees Cook +Reviewed-by: Andy Shevchenko +Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com +Signed-off-by: Jani Nikula +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + include/linux/string.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/include/linux/string.h b/include/linux/string.h +index 0cef345a6e87a..98b053d9c7005 100644 +--- a/include/linux/string.h ++++ b/include/linux/string.h +@@ -170,6 +170,18 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt) + void *memchr_inv(const void *s, int c, size_t n); + char *strreplace(char *s, char old, char new); + ++/** ++ * mem_is_zero - Check if an area of memory is all 0's. ++ * @s: The memory area ++ * @n: The size of the area ++ * ++ * Return: True if the area of memory is all 0's. ++ */ ++static inline bool mem_is_zero(const void *s, size_t n) ++{ ++ return !memchr_inv(s, 0, n); ++} ++ + extern void kfree_const(const void *x); + + extern char *kstrdup(const char *s, gfp_t gfp) __malloc; +-- +2.53.0 + diff --git a/queue-5.10/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-5.10/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..5629d05bdb --- /dev/null +++ b/queue-5.10/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 4a3d135627c2cf2bdf09633a01ad77e1f18bd78f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index 381d7e3989ada..32f9ab82a8810 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1105,10 +1105,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-5.15/arm-integrator-fix-early-initialization.patch b/queue-5.15/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..e0227143ff --- /dev/null +++ b/queue-5.15/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From f41d23c53a0e029b93c790034bc0fb1602037c8e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-integrator/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c +index b7eb4038798b6..b6d54cee5b792 100644 +--- a/arch/arm/mach-integrator/integrator_cp.c ++++ b/arch/arm/mach-integrator/integrator_cp.c +@@ -88,14 +88,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -121,6 +113,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -140,7 +136,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-5.15/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-5.15/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..c2c370acb6 --- /dev/null +++ b/queue-5.15/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From 5aed049c33a8f571c00040a8389c4121b36e5815 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 38e1efb20aef5..3bd46fb38d5f6 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4442,10 +4442,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4453,7 +4467,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4470,7 +4483,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4478,8 +4491,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-5.15/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-5.15/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..86b724413c --- /dev/null +++ b/queue-5.15/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,51 @@ +From 3d315fc3805d976b6d4a3c5fcb9d5aa00aaacee9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index ef4da3f0cd22d..ede7510562f9d 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -262,14 +262,15 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, size_t len, int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + /* The arm-smmu driver expects the addresses to be sign extended */ + if (iova & BIT_ULL(48)) + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-5.15/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-5.15/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..42d7130531 --- /dev/null +++ b/queue-5.15/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From dd17527fc96f2ce1400a8dace1efc0c6aa84aef5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index badafcd61998f..e3615a1339ade 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -7,7 +7,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -17,11 +17,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + if (!(*reg)) + *reg = kvzalloc(len_padded, GFP_KERNEL); +@@ -49,8 +49,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -59,6 +59,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -68,6 +69,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -182,7 +194,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-5.15/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-5.15/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..9a883a0be3 --- /dev/null +++ b/queue-5.15/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From 1cc14017eb4b03567d57db16a579d23b7bbc6b69 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-5.15/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-5.15/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..0be9a24ee2 --- /dev/null +++ b/queue-5.15/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From ca4f0ca06c0e65a406f110d07b14ee258b555fb7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index 27820a59ce25e..93962334b45cc 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -24,6 +24,8 @@ static int ffa_device_match(struct device *dev, struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -107,7 +109,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-5.15/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-5.15/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..f2202a73f5 --- /dev/null +++ b/queue-5.15/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From dffc2212da84678367264abb1854538267f43d72 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e4fb0c1ae4869..34351f8c4d6f3 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -687,7 +687,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-5.15/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-5.15/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..c148e5b13f --- /dev/null +++ b/queue-5.15/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From 472d992fe9c8671a32f917ee745118e93348165b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 58ad8328bedc2..b1e4571401c9a 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1011,6 +1011,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1018,9 +1019,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-5.15/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch b/queue-5.15/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch new file mode 100644 index 0000000000..9f4726e618 --- /dev/null +++ b/queue-5.15/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch @@ -0,0 +1,69 @@ +From 5a7aa32abce2242deec2b91503e9e85e48059952 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Nov 2024 22:16:15 +0200 +Subject: gpiolib: cdev: use !mem_is_zero() instead of memchr_inv(s, 0, n) + +From: Andy Shevchenko + +[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ] + +Use the mem_is_zero() helper where possible. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index d4b221c90bb20..58ad8328bedc2 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -14,13 +14,13 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -1018,7 +1018,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + +- if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) ++ if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + + for (i = 0; i < num_lines; i++) { +@@ -1437,7 +1437,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) + return -EINVAL; + +- if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) ++ if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) + return -EINVAL; + + lc = &ulr.config; +@@ -2202,7 +2202,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +- if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) ++ if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) + return -EINVAL; + + desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); +-- +2.53.0 + diff --git a/queue-5.15/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-5.15/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..1ff4c9d904 --- /dev/null +++ b/queue-5.15/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 560f3c14185c26090fd6b652a3c0c716fd67ee28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 9eb4d02cc6d77..6e9501fe1a281 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -222,7 +222,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-5.15/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-5.15/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..d35bce87f0 --- /dev/null +++ b/queue-5.15/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From 2d17010257fc5c523895545bef500ad4db01c674 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index dd4195e964faf..b415e375d6205 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -450,14 +450,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + enum ice_status ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-5.15/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch b/queue-5.15/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch new file mode 100644 index 0000000000..8bff5e54e9 --- /dev/null +++ b/queue-5.15/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch @@ -0,0 +1,57 @@ +From f68c7c1bd867898911025a2e73427a100cf49c53 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 23:03:28 -0400 +Subject: ipv6: route: Unregister netdevice notifier on BPF init failure + +From: Yuho Choi + +[ Upstream commit 1341db322417266fb5845df81d28305b83a37324 ] + +ip6_route_init() registers ip6_route_dev_notifier before registering the +IPv6 route BPF iterator target. If bpf_iter_register() fails after the +notifier has been registered, the error path currently jumps to +out_register_late_subsys and unwinds the RTNL handlers and pernet route +state without removing the notifier from the netdevice notifier chain. + +This leaves ip6_route_dev_notify() callable after the IPv6 route state it +uses has been torn down. Add a separate unwind label for the BPF iterator +failure path and unregister the netdevice notifier before continuing with +the existing cleanup. + +Fixes: 138d0be35b14 ("net: bpf: Add netlink and ipv6_route bpf_iter targets") +Signed-off-by: Yuho Choi +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 52e8e77df69a1..ad21cdf8045a0 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6744,7 +6744,7 @@ int __init ip6_route_init(void) + #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) + ret = bpf_iter_register(); + if (ret) +- goto out_register_late_subsys; ++ goto out_register_notifier; + #endif + #endif + +@@ -6758,6 +6758,10 @@ int __init ip6_route_init(void) + out: + return ret; + ++#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) ++out_register_notifier: ++ unregister_netdevice_notifier(&ip6_route_dev_notifier); ++#endif + out_register_late_subsys: + rtnl_unregister_all(PF_INET6); + unregister_pernet_subsys(&ip6_route_net_late_ops); +-- +2.53.0 + diff --git a/queue-5.15/irqchip-ath79-cpu-remove-unused-function.patch b/queue-5.15/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..ea0577d255 --- /dev/null +++ b/queue-5.15/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From d561bdd966cee9cb3c1d746c38bb2d611ab71c15 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-5.15/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-5.15/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..8e581e48c3 --- /dev/null +++ b/queue-5.15/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From 5ba94db47a0c89c4cce4145bf4b2b3c694e3f1b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 0b5dfb001bacc..a32943555b67d 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-5.15/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-5.15/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..fa94014a2a --- /dev/null +++ b/queue-5.15/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From 427aaefb505d01e651f288b8ca3a61a6e3bcf8ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index a32943555b67d..b27ef9f1af1d3 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-5.15/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-5.15/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..9700a3f642 --- /dev/null +++ b/queue-5.15/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From 54200a739b058d02fd020c1562b68c9e53ecd177 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index d5be9ca4d4fed..411f94e95291c 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1933,6 +1933,9 @@ static int ag71xx_probe(struct platform_device *pdev) + return -ENOMEM; + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-5.15/net-bridge-flush-multicast-groups-when-snooping-is-d.patch b/queue-5.15/net-bridge-flush-multicast-groups-when-snooping-is-d.patch new file mode 100644 index 0000000000..61c526d348 --- /dev/null +++ b/queue-5.15/net-bridge-flush-multicast-groups-when-snooping-is-d.patch @@ -0,0 +1,67 @@ +From ed797798e1d033f3a1008a1c5501942e131b1b55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Oct 2025 16:45:37 +0200 +Subject: net: bridge: Flush multicast groups when snooping is disabled + +From: Petr Machata + +[ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] + +When forwarding multicast packets, the bridge takes MDB into account when +IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the +MDB is retained, even though it is not used anymore. + +At the same time, during the time that snooping is disabled, the IGMP / MLD +control packets are obviously ignored, and after the snooping is reenabled, +the administrator has to assume it is out of sync. In particular, missed +join and leave messages would lead to traffic being forwarded to wrong +interfaces. + +Keeping the MDB entries around thus serves no purpose, and just takes +memory. Note also that disabling per-VLAN snooping does actually flush the +relevant MDB entries. + +This patch flushes non-permanent MDB entries as global snooping is +disabled. + +Signed-off-by: Petr Machata +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index b8fb1e23b107e..38e1efb20aef5 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4442,6 +4442,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + ++static void br_multicast_del_grps(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_disable_port_ctx(&port->multicast_ctx); ++} ++ + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +@@ -4462,6 +4470,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; ++ br_multicast_del_grps(br); + goto unlock; + } + +-- +2.53.0 + diff --git a/queue-5.15/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-5.15/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..017cc72a5c --- /dev/null +++ b/queue-5.15/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From 40eedc94f205225e46f3d505ab2e082bc52afa6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index abd61514d3361..f0b2510bff15b 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -907,12 +907,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2357,6 +2361,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2536,6 +2542,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-5.15/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-5.15/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..36ee29403c --- /dev/null +++ b/queue-5.15/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From 3b2b5c941170db0119f48bbdd6bac5c28df2b72b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index fbee11e8f6e0c..118e72c845e02 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1183,37 +1183,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-5.15/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch b/queue-5.15/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch new file mode 100644 index 0000000000..c4adc01a22 --- /dev/null +++ b/queue-5.15/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch @@ -0,0 +1,185 @@ +From daad5d06bdaac438999e78f9254b2ca8061e5e86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Apr 2024 10:15:11 +0300 +Subject: net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to mt753x_to_cpu_fw +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Arınç ÜNAL + +[ Upstream commit 7603a0c7d2210a253265394b50567c64fbb977e4 ] + +The mt753x_bpdu_port_fw enum is globally used for manipulating the process +of deciding the forwardable ports, specifically concerning the CPU port(s). +Therefore, rename it and the values in it to mt753x_to_cpu_fw. + +Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents. + +Signed-off-by: Arınç ÜNAL +Signed-off-by: David S. Miller +Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames") +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 44 ++++++++++------------- + drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++-------------------- + 2 files changed, 56 insertions(+), 64 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index f0b2510bff15b..fbee11e8f6e0c 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1187,42 +1187,34 @@ mt753x_trap_frames(struct mt7530_priv *priv) + * VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_BPC, +- MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK | +- MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK | +- MT753X_BPDU_PORT_FW_MASK, +- MT753X_PAE_BPDU_FR | +- MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | ++ BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC1, +- MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK | +- MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK, +- MT753X_R02_BPDU_FR | +- MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | ++ R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | ++ R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC2, +- MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK | +- MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK, +- MT753X_R0E_BPDU_FR | +- MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | ++ R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | ++ R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + } + + static int +diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h +index 4a013680ce643..9e76a2b6403b5 100644 +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -67,47 +67,47 @@ enum mt753x_id { + #define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + +-/* Registers for BPDU and PAE frame control*/ ++/* Register for BPDU and PAE frame control */ + #define MT753X_BPC 0x24 +-#define MT753X_PAE_BPDU_FR BIT(25) +-#define MT753X_PAE_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_PAE_EG_TAG(x) FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x) +-#define MT753X_PAE_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_PAE_PORT_FW(x) FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) +-#define MT753X_BPDU_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_BPDU_EG_TAG(x) FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x) +-#define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :01 and :02 MAC DA frame control */ ++#define PAE_BPDU_FR BIT(25) ++#define PAE_EG_TAG_MASK GENMASK(24, 22) ++#define PAE_EG_TAG(x) FIELD_PREP(PAE_EG_TAG_MASK, x) ++#define PAE_PORT_FW_MASK GENMASK(18, 16) ++#define PAE_PORT_FW(x) FIELD_PREP(PAE_PORT_FW_MASK, x) ++#define BPDU_EG_TAG_MASK GENMASK(8, 6) ++#define BPDU_EG_TAG(x) FIELD_PREP(BPDU_EG_TAG_MASK, x) ++#define BPDU_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */ + #define MT753X_RGAC1 0x28 +-#define MT753X_R02_BPDU_FR BIT(25) +-#define MT753X_R02_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R02_EG_TAG(x) FIELD_PREP(MT753X_R02_EG_TAG_MASK, x) +-#define MT753X_R02_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R02_PORT_FW(x) FIELD_PREP(MT753X_R02_PORT_FW_MASK, x) +-#define MT753X_R01_BPDU_FR BIT(9) +-#define MT753X_R01_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R01_EG_TAG(x) FIELD_PREP(MT753X_R01_EG_TAG_MASK, x) +-#define MT753X_R01_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :03 and :0E MAC DA frame control */ ++#define R02_BPDU_FR BIT(25) ++#define R02_EG_TAG_MASK GENMASK(24, 22) ++#define R02_EG_TAG(x) FIELD_PREP(R02_EG_TAG_MASK, x) ++#define R02_PORT_FW_MASK GENMASK(18, 16) ++#define R02_PORT_FW(x) FIELD_PREP(R02_PORT_FW_MASK, x) ++#define R01_BPDU_FR BIT(9) ++#define R01_EG_TAG_MASK GENMASK(8, 6) ++#define R01_EG_TAG(x) FIELD_PREP(R01_EG_TAG_MASK, x) ++#define R01_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */ + #define MT753X_RGAC2 0x2c +-#define MT753X_R0E_BPDU_FR BIT(25) +-#define MT753X_R0E_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R0E_EG_TAG(x) FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x) +-#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) +-#define MT753X_R03_BPDU_FR BIT(9) +-#define MT753X_R03_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R03_EG_TAG(x) FIELD_PREP(MT753X_R03_EG_TAG_MASK, x) +-#define MT753X_R03_PORT_FW_MASK GENMASK(2, 0) +- +-enum mt753x_bpdu_port_fw { +- MT753X_BPDU_FOLLOW_MFC, +- MT753X_BPDU_CPU_EXCLUDE = 4, +- MT753X_BPDU_CPU_INCLUDE = 5, +- MT753X_BPDU_CPU_ONLY = 6, +- MT753X_BPDU_DROP = 7, ++#define R0E_BPDU_FR BIT(25) ++#define R0E_EG_TAG_MASK GENMASK(24, 22) ++#define R0E_EG_TAG(x) FIELD_PREP(R0E_EG_TAG_MASK, x) ++#define R0E_PORT_FW_MASK GENMASK(18, 16) ++#define R0E_PORT_FW(x) FIELD_PREP(R0E_PORT_FW_MASK, x) ++#define R03_BPDU_FR BIT(9) ++#define R03_EG_TAG_MASK GENMASK(8, 6) ++#define R03_EG_TAG(x) FIELD_PREP(R03_EG_TAG_MASK, x) ++#define R03_PORT_FW_MASK GENMASK(2, 0) ++ ++enum mt753x_to_cpu_fw { ++ TO_CPU_FW_SYSTEM_DEFAULT, ++ TO_CPU_FW_CPU_EXCLUDE = 4, ++ TO_CPU_FW_CPU_INCLUDE = 5, ++ TO_CPU_FW_CPU_ONLY = 6, ++ TO_CPU_FW_DROP = 7, + }; + + /* Registers for address table access */ +-- +2.53.0 + diff --git a/queue-5.15/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch b/queue-5.15/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch new file mode 100644 index 0000000000..6204bdea9e --- /dev/null +++ b/queue-5.15/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch @@ -0,0 +1,58 @@ +From b5b636fb21157ca6ca53c3db25d064b07bcd2abe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 22 Apr 2025 04:10:20 +0100 +Subject: net: dsa: mt7530: sync driver-specific behavior of MT7531 variants + +From: Daniel Golle + +[ Upstream commit 497041d763016c2e8314d2f6a329a9b77c3797ca ] + +MT7531 standalone and MMIO variants found in MT7988 and EN7581 share +most basic properties. Despite that, assisted_learning_on_cpu_port and +mtu_enforcement_ingress were only applied for MT7531 but not for MT7988 +or EN7581, causing the expected issues on MMIO devices. + +Apply both settings equally also for MT7988 and EN7581 by moving both +assignments form mt7531_setup() to mt7531_setup_common(). + +This fixes unwanted flooding of packets due to unknown unicast +during DA lookup, as well as issues with heterogenous MTU settings. + +Fixes: 7f54cc9772ce ("net: dsa: mt7530: split-off common parts from mt7531_setup") +Signed-off-by: Daniel Golle +Reviewed-by: Chester A. Unal +Link: https://patch.msgid.link/89ed7ec6d4fa0395ac53ad2809742bb1ce61ed12.1745290867.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: e824e40d0e84 ("net: dsa: mt7530: fix FDB entries not aging out with short timeout") +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 86db6a18c8377..abd61514d3361 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2534,6 +2534,9 @@ mt7531_setup_common(struct dsa_switch *ds) + struct mt7530_priv *priv = ds->priv; + int ret, i; + ++ ds->assisted_learning_on_cpu_port = true; ++ ds->mtu_enforcement_ingress = true; ++ + mt753x_trap_frames(priv); + + /* Enable and reset MIB counters */ +@@ -2673,9 +2676,6 @@ mt7531_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- ds->assisted_learning_on_cpu_port = true; +- ds->mtu_enforcement_ingress = true; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.15/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-5.15/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..95093805fc --- /dev/null +++ b/queue-5.15/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 9c9f0a8e7c838801c591a2d6cbbc86a71d83a6bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index be2b106b4aa27..8cc3cca1f4bd4 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -121,6 +121,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1413,6 +1414,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1426,7 +1428,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1466,6 +1467,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1476,6 +1478,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1510,6 +1513,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1518,6 +1522,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1527,6 +1532,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1856,6 +1862,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-5.15/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-5.15/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..f278e1a4be --- /dev/null +++ b/queue-5.15/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From a87701aa72922c4bdcb2049836d51f5f3e93323d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 29f8e19661efa..be2b106b4aa27 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1462,6 +1462,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-5.15/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-5.15/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..a7ae0153f5 --- /dev/null +++ b/queue-5.15/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From c470065fda6b69bb45b30571da1c945ad1c0f7de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 3a11dccec8c1b..29f8e19661efa 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -120,6 +120,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1411,10 +1413,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1468,6 +1470,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1518,6 +1521,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1846,6 +1850,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-5.15/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-5.15/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..34c5ace94e --- /dev/null +++ b/queue-5.15/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From e81737519b0efb6ed95562b8bcb10e8fe726045d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index d0c4c8b7a15ab..9795f959c9bde 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1270,7 +1270,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1297,7 +1296,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-5.15/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-5.15/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..f2246dcefa --- /dev/null +++ b/queue-5.15/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 73dba20cbe14d490a919e48915380212224d01fe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index efd7ae1bab43c..91b1af1d72eb8 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -75,21 +75,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -192,6 +190,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -210,13 +209,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-5.15/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-5.15/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..ea28216066 --- /dev/null +++ b/queue-5.15/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From d7743dc2d4bcf152a65eab80676c9abb16996ce9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 91b1af1d72eb8..f2542bb9254fc 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -206,6 +206,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-5.15/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-5.15/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..59a89a3c06 --- /dev/null +++ b/queue-5.15/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From ef8b2f35ee1f5eb1637709b39467efaef0fca6d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 002806908aa82..33b8afa2048ce 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -784,11 +784,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-5.15/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-5.15/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..a616e1cdce --- /dev/null +++ b/queue-5.15/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From 052bb59c61665f3a0d7c405b2402bf4b212372a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 33b8afa2048ce..630fa387da14f 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -773,21 +773,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-5.15/netfilter-arp_tables-allow-use-of-arpt_do_table-as-h.patch b/queue-5.15/netfilter-arp_tables-allow-use-of-arpt_do_table-as-h.patch new file mode 100644 index 0000000000..1447923996 --- /dev/null +++ b/queue-5.15/netfilter-arp_tables-allow-use-of-arpt_do_table-as-h.patch @@ -0,0 +1,87 @@ +From f307f70bbbf56368343e011e2c3a141d8548ef50 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 Oct 2021 17:15:12 +0200 +Subject: netfilter: arp_tables: allow use of arpt_do_table as hookfn + +From: Florian Westphal + +[ Upstream commit e8d225b6002673366abc2e40e30c991bdc8d62ca ] + +This is possible now that the xt_table structure is passed in via *priv. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter_arp/arp_tables.h | 5 ++--- + net/ipv4/netfilter/arp_tables.c | 7 ++++--- + net/ipv4/netfilter/arptable_filter.c | 10 +--------- + 3 files changed, 7 insertions(+), 15 deletions(-) + +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index 4f9a4b3c58926..a40aaf645fa47 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -54,9 +54,8 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); + void arpt_unregister_table_pre_exit(struct net *net, const char *name); +-extern unsigned int arpt_do_table(struct sk_buff *skb, +- const struct nf_hook_state *state, +- struct xt_table *table); ++extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, ++ const struct nf_hook_state *state); + + #ifdef CONFIG_NETFILTER_XTABLES_COMPAT + #include +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 92bc90ee76748..564054123772a 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -191,10 +191,11 @@ struct arpt_entry *arpt_next_entry(const struct arpt_entry *entry) + return (void *)entry + entry->next_offset; + } + +-unsigned int arpt_do_table(struct sk_buff *skb, +- const struct nf_hook_state *state, +- struct xt_table *table) ++unsigned int arpt_do_table(void *priv, ++ struct sk_buff *skb, ++ const struct nf_hook_state *state) + { ++ const struct xt_table *table = priv; + unsigned int hook = state->hook; + static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); + unsigned int verdict = NF_DROP; +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 771eec4629352..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -26,14 +26,6 @@ static const struct xt_table packet_filter = { + .priority = NF_IP_PRI_FILTER, + }; + +-/* The work comes in here from netfilter.c */ +-static unsigned int +-arptable_filter_hook(void *priv, struct sk_buff *skb, +- const struct nf_hook_state *state) +-{ +- return arpt_do_table(skb, state, priv); +-} +- + static struct nf_hook_ops *arpfilter_ops __read_mostly; + + static int arptable_filter_table_init(struct net *net) +@@ -72,7 +64,7 @@ static int __init arptable_filter_init(void) + if (ret < 0) + return ret; + +- arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arptable_filter_hook); ++ arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); + if (IS_ERR(arpfilter_ops)) { + xt_unregister_template(&packet_filter); + return PTR_ERR(arpfilter_ops); +-- +2.53.0 + diff --git a/queue-5.15/netfilter-arptables-allow-xtables-nft-only-builds.patch b/queue-5.15/netfilter-arptables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..423312d52e --- /dev/null +++ b/queue-5.15/netfilter-arptables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,82 @@ +From 1f1b596c154d38f81561118087a37754d66453be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 23 Jan 2024 16:42:48 +0100 +Subject: netfilter: arptables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 4654467dc7e111e84f43ed1b70322873ae77e7be ] + +Allows to build kernel that supports the arptables mangle target +via nftables' compat infra but without the arptables get/setsockopt +interface or the old arptables filter interpreter. + +IOW, setting IP_NF_ARPFILTER=n will break arptables-legacy, but +arptables-nft will continue to work as long as nftables compat +support is enabled. + +Signed-off-by: Florian Westphal +Reviewed-by: Phil Sutter +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 63cb953bd0196..5c2cdcb19dba3 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -331,36 +331,34 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate "ARP tables support" +- select NETFILTER_XTABLES +- select NETFILTER_FAMILY_ARP +- depends on NETFILTER_ADVANCED +- help +- arptables is a general, extensible packet identification framework. +- The ARP packet filtering and mangling (manipulation)subsystems +- use this: say Y or M here if you want to use either of those. +- +- To compile it as a module, choose M here. If unsure, say N. ++ tristate + +-if IP_NF_ARPTABLES ++config NFT_COMPAT_ARP ++ tristate ++ depends on NF_TABLES_ARP && NFT_COMPAT ++ default m if NFT_COMPAT=m ++ default y if NFT_COMPAT=y + + config IP_NF_ARPFILTER +- tristate "ARP packet filtering" ++ tristate "arptables-legacy packet filtering support" ++ select IP_NF_ARPTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +- local output. On a bridge, you can also specify filtering rules +- for forwarded ARP packets. See the man page for arptables(8). ++ local output. This is only needed for arptables-legacy(8). ++ Neither arptables-nft nor nftables need this to work. + + To compile it as a module, choose M here. If unsure, say N. + + config IP_NF_ARP_MANGLE + tristate "ARP payload mangling" ++ depends on IP_NF_ARPTABLES || NFT_COMPAT_ARP + help + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + +-endif # IP_NF_ARPTABLES ++ This option is needed by both arptables-legacy and arptables-nft. ++ It is not used by nftables. + + endmenu + +-- +2.53.0 + diff --git a/queue-5.15/netfilter-arptables-select-netfilter_family_arp-when.patch b/queue-5.15/netfilter-arptables-select-netfilter_family_arp-when.patch new file mode 100644 index 0000000000..74de54eb00 --- /dev/null +++ b/queue-5.15/netfilter-arptables-select-netfilter_family_arp-when.patch @@ -0,0 +1,112 @@ +From dd443750c3981759c31944f3d8beb0ada5df4f0b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Mar 2024 21:15:52 -0700 +Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building + arp_tables.c + +From: Kuniyuki Iwashima + +[ Upstream commit 15fba562f7a9f04322b8bfc8f392e04bb93d81be ] + +syzkaller started to report a warning below [0] after consuming the +commit 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only +builds"). + +The change accidentally removed the dependency on NETFILTER_FAMILY_ARP +from IP_NF_ARPTABLES. + +If NF_TABLES_ARP is not enabled on Kconfig, NETFILTER_FAMILY_ARP will +be removed and some code necessary for arptables will not be compiled. + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + CONFIG_NETFILTER_FAMILY_ARP=y + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + + $ make olddefconfig + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + +So, when nf_register_net_hooks() is called for arptables, it will +trigger the splat below. + +Now IP_NF_ARPTABLES is only enabled by IP_NF_ARPFILTER, so let's +restore the dependency on NETFILTER_FAMILY_ARP in IP_NF_ARPFILTER. + +[0]: +WARNING: CPU: 0 PID: 242 at net/netfilter/core.c:316 nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Modules linked in: +CPU: 0 PID: 242 Comm: syz-executor.0 Not tainted 6.8.0-12821-g537c2e91d354 #10 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 +RIP: 0010:nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Code: 83 fd 04 0f 87 bc 00 00 00 e8 5b 84 83 fd 4d 8d ac ec a8 0b 00 00 e8 4e 84 83 fd 4c 89 e8 5b 5d 41 5c 41 5d c3 e8 3f 84 83 fd <0f> 0b e8 38 84 83 fd 45 31 ed 5b 5d 4c 89 e8 41 5c 41 5d c3 e8 26 +RSP: 0018:ffffc90000b8f6e8 EFLAGS: 00010293 +RAX: 0000000000000000 RBX: 0000000000000003 RCX: ffffffff83c42164 +RDX: ffff888106851180 RSI: ffffffff83c42321 RDI: 0000000000000005 +RBP: 0000000000000000 R08: 0000000000000005 R09: 000000000000000a +R10: 0000000000000003 R11: ffff8881055c2f00 R12: ffff888112b78000 +R13: 0000000000000000 R14: ffff8881055c2f00 R15: ffff8881055c2f00 +FS: 00007f377bd78800(0000) GS:ffff88811b000000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 0000000000496068 CR3: 000000011298b003 CR4: 0000000000770ef0 +PKRU: 55555554 +Call Trace: + + __nf_register_net_hook+0xcd/0x7a0 net/netfilter/core.c:428 + nf_register_net_hook+0x116/0x170 net/netfilter/core.c:578 + nf_register_net_hooks+0x5d/0xc0 net/netfilter/core.c:594 + arpt_register_table+0x250/0x420 net/ipv4/netfilter/arp_tables.c:1553 + arptable_filter_table_init+0x41/0x60 net/ipv4/netfilter/arptable_filter.c:39 + xt_find_table_lock+0x2e9/0x4b0 net/netfilter/x_tables.c:1260 + xt_request_find_table_lock+0x2b/0xe0 net/netfilter/x_tables.c:1285 + get_info+0x169/0x5c0 net/ipv4/netfilter/arp_tables.c:808 + do_arpt_get_ctl+0x3f9/0x830 net/ipv4/netfilter/arp_tables.c:1444 + nf_getsockopt+0x76/0xd0 net/netfilter/nf_sockopt.c:116 + ip_getsockopt+0x17d/0x1c0 net/ipv4/ip_sockglue.c:1777 + tcp_getsockopt+0x99/0x100 net/ipv4/tcp.c:4373 + do_sock_getsockopt+0x279/0x360 net/socket.c:2373 + __sys_getsockopt+0x115/0x1e0 net/socket.c:2402 + __do_sys_getsockopt net/socket.c:2412 [inline] + __se_sys_getsockopt net/socket.c:2409 [inline] + __x64_sys_getsockopt+0xbd/0x150 net/socket.c:2409 + do_syscall_x64 arch/x86/entry/common.c:52 [inline] + do_syscall_64+0x4f/0x110 arch/x86/entry/common.c:83 + entry_SYSCALL_64_after_hwframe+0x46/0x4e +RIP: 0033:0x7f377beca6fe +Code: 1f 44 00 00 48 8b 15 01 97 0a 00 f7 d8 64 89 02 b8 ff ff ff ff eb b8 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 37 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 c9 +RSP: 002b:00000000005df728 EFLAGS: 00000246 ORIG_RAX: 0000000000000037 +RAX: ffffffffffffffda RBX: 00000000004966e0 RCX: 00007f377beca6fe +RDX: 0000000000000060 RSI: 0000000000000000 RDI: 0000000000000003 +RBP: 000000000042938a R08: 00000000005df73c R09: 00000000005df800 +R10: 00000000004966e8 R11: 0000000000000246 R12: 0000000000000003 +R13: 0000000000496068 R14: 0000000000000003 R15: 00000000004bc9d8 + + +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Reported-by: syzkaller +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Simon Horman +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 18f60e675c438..e752a07a871fe 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -351,6 +351,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ select NETFILTER_FAMILY_ARP + depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of +-- +2.53.0 + diff --git a/queue-5.15/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-5.15/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..453dfa4b65 --- /dev/null +++ b/queue-5.15/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From 76719a23d5c96d4690773efa9826f16243e7fcf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index 5390b25cdb45e..9374a3207a276 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2582,19 +2582,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-5.15/netfilter-ebtables-allow-xtables-nft-only-builds.patch b/queue-5.15/netfilter-ebtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..681699bb96 --- /dev/null +++ b/queue-5.15/netfilter-ebtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,84 @@ +From 4b30a7c2d72444a5dd148c1d12a20ff29aab15bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:12 +0100 +Subject: netfilter: ebtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 7ad269787b6615ca56bb161063331991fce51abf ] + +Same patch as previous one, but for ebtables. + +To build a kernel that only supports ebtables-nft, the builtin tables +need to be disabled, i.e.: + +CONFIG_BRIDGE_EBT_BROUTE=n +CONFIG_BRIDGE_EBT_T_FILTER=n +CONFIG_BRIDGE_EBT_T_NAT=n + +The ebtables specific extensions can then be used nftables' +NFT_COMPAT interface. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 7 +++++++ + net/bridge/netfilter/Makefile | 2 +- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 7f304a19ac1bf..104c0125e32e8 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -39,6 +39,10 @@ config NF_CONNTRACK_BRIDGE + + To compile it as a module, choose M here. If unsure, say N. + ++# old sockopt interface and eval loop ++config BRIDGE_NF_EBTABLES_LEGACY ++ tristate ++ + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" + depends on BRIDGE && NETFILTER && NETFILTER_XTABLES +@@ -55,6 +59,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -65,6 +70,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -74,6 +80,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile +index 1c9ce49ab6513..b9a1303da9771 100644 +--- a/net/bridge/netfilter/Makefile ++++ b/net/bridge/netfilter/Makefile +@@ -9,7 +9,7 @@ obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o + # connection tracking + obj-$(CONFIG_NF_CONNTRACK_BRIDGE) += nf_conntrack_bridge.o + +-obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o ++obj-$(CONFIG_BRIDGE_NF_EBTABLES_LEGACY) += ebtables.o + + # tables + obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o +-- +2.53.0 + diff --git a/queue-5.15/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-5.15/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..067e7a3020 --- /dev/null +++ b/queue-5.15/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From a7e863cc557ce860b44115390ed12d114f47ea0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index c5d6fb937394c..d54afb88761e6 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index ee3d6d5a03a35..28f6a1f33898a 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -100,18 +100,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index c98840b68fc52..a9450d6e49565 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -99,16 +99,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-5.15/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-5.15/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..5b507e3942 --- /dev/null +++ b/queue-5.15/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From ef22989140ba8137a7d5f9add8a21de6ca6eabfb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 3d4ea774d7e8f..c5d6fb937394c 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 257d63b5dec16..ee3d6d5a03a35 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -116,8 +116,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 39179c2cf87d2..c98840b68fc52 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -115,8 +115,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index c74efcc2b4996..5390b25cdb45e 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2555,11 +2559,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-5.15/netfilter-exclude-legacy-tables-on-preempt_rt.patch b/queue-5.15/netfilter-exclude-legacy-tables-on-preempt_rt.patch new file mode 100644 index 0000000000..868f397e12 --- /dev/null +++ b/queue-5.15/netfilter-exclude-legacy-tables-on-preempt_rt.patch @@ -0,0 +1,335 @@ +From f36d8452557e5a33aa0f8d50e331733865ad14c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Jun 2025 17:44:23 +0200 +Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT. + +From: Pablo Neira Ayuso + +[ Upstream commit 9fce66583f06c212e95e4b76dd61d8432ffa56b6 ] + +The seqcount xt_recseq is used to synchronize the replacement of +xt_table::private in xt_replace_table() against all readers such as +ipt_do_table() + +To ensure that there is only one writer, the writing side disables +bottom halves. The sequence counter can be acquired recursively. Only the +first invocation modifies the sequence counter (signaling that a writer +is in progress) while the following (recursive) writer does not modify +the counter. +The lack of a proper locking mechanism for the sequence counter can lead +to live lock on PREEMPT_RT if the high prior reader preempts the +writer. Additionally if the per-CPU lock on PREEMPT_RT is removed from +local_bh_disable() then there is no synchronisation for the per-CPU +sequence counter. + +The affected code is "just" the legacy netfilter code which is replaced +by "netfilter tables". That code can be disabled without sacrificing +functionality because everything is provided by the newer +implementation. This will only requires the usage of the "-nft" tools +instead of the "-legacy" ones. +The long term plan is to remove the legacy code so lets accelerate the +progress. + +Relax dependencies on iptables legacy, replace select with depends on, +this should cause no harm to existing kernel configs and users can still +toggle IP{6}_NF_IPTABLES_LEGACY in any case. +Make EBTABLES_LEGACY, IPTABLES_LEGACY and ARPTABLES depend on +NETFILTER_XTABLES_LEGACY. Hide xt_recseq and its users, +xt_register_table() and xt_percpu_counter_alloc() behind +NETFILTER_XTABLES_LEGACY. Let NETFILTER_XTABLES_LEGACY depend on +!PREEMPT_RT. + +This will break selftest expecing the legacy options enabled and will be +addressed in a following patch. + +Co-developed-by: Florian Westphal +Co-developed-by: Sebastian Andrzej Siewior +Signed-off-by: Sebastian Andrzej Siewior +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 10 +++++----- + net/ipv4/netfilter/Kconfig | 24 ++++++++++++------------ + net/ipv6/netfilter/Kconfig | 19 +++++++++---------- + net/netfilter/Kconfig | 10 ++++++++++ + net/netfilter/x_tables.c | 16 +++++++++++----- + 5 files changed, 47 insertions(+), 32 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index f16bbbbb94817..60f28e4fb5c0a 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -42,8 +42,8 @@ config NF_CONNTRACK_BRIDGE + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY + tristate "Legacy EBTABLES support" +- depends on BRIDGE && NETFILTER_XTABLES +- default n ++ depends on BRIDGE && NETFILTER_XTABLES_LEGACY ++ default n + help + Legacy ebtables packet/frame classifier. + This is not needed if you are using ebtables over nftables +@@ -65,7 +65,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -76,7 +76,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -86,7 +86,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 2e540786f9512..4cfe4b12bda7c 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -13,8 +13,8 @@ config NF_DEFRAG_IPV4 + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY + tristate "Legacy IP tables support" +- default n +- select NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + iptables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -190,8 +190,8 @@ config IP_NF_MATCH_TTL + # `filter', generic and specific targets + config IP_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -228,10 +228,10 @@ config IP_NF_TARGET_SYNPROXY + config IP_NF_NAT + tristate "iptables NAT support" + depends on NF_CONNTRACK ++ depends on IP_NF_IPTABLES_LEGACY + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -271,8 +271,8 @@ endif # IP_NF_NAT + # mangle + specific targets + config IP_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -321,7 +321,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -335,7 +335,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -347,8 +347,8 @@ endif # IP_NF_IPTABLES + # ARP tables + config IP_NF_ARPTABLES + tristate "Legacy ARPTABLES support" +- depends on NETFILTER_XTABLES +- default n ++ depends on NETFILTER_XTABLES_LEGACY ++ default n + help + arptables is a legacy packet classifier. + This is not needed if you are using arptables over nftables +@@ -364,7 +364,7 @@ config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES + select NETFILTER_FAMILY_ARP +- depends on NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index 670d23f926e62..052f1f53c4dfe 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -9,9 +9,8 @@ menu "IPv6: Netfilter Configuration" + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY + tristate "Legacy IP6 tables support" +- depends on INET && IPV6 +- select NETFILTER_XTABLES +- default n ++ depends on INET && IPV6 && NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + ip6tables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -204,8 +203,8 @@ config IP6_NF_TARGET_HL + + config IP6_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + tristate + help + Packet filtering defines a table `filter', which has a series of +@@ -241,8 +240,8 @@ config IP6_NF_TARGET_SYNPROXY + + config IP6_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -252,7 +251,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -266,7 +265,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -277,8 +276,8 @@ config IP6_NF_NAT + tristate "ip6tables NAT support" + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED ++ depends on IP6_NF_IPTABLES_LEGACY + select NF_NAT +- select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index fdfda4b6bff67..085ea824c503d 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -756,6 +756,16 @@ config NETFILTER_XTABLES_COMPAT + + If unsure, say N. + ++config NETFILTER_XTABLES_LEGACY ++ bool "Netfilter legacy tables support" ++ depends on !PREEMPT_RT ++ help ++ Say Y here if you still require support for legacy tables. This is ++ required by the legacy tools (iptables-legacy) and is not needed if ++ you use iptables over nftables (iptables-nft). ++ Legacy support is not limited to IP, it also includes EBTABLES and ++ ARPTABLES. ++ + comment "Xtables combined modules" + + config NETFILTER_XT_MARK +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 9c0ec0bbb5699..30af321d6c964 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1339,12 +1339,13 @@ void xt_compat_unlock(u_int8_t af) + EXPORT_SYMBOL_GPL(xt_compat_unlock); + #endif + +-DEFINE_PER_CPU(seqcount_t, xt_recseq); +-EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); +- + struct static_key xt_tee_enabled __read_mostly; + EXPORT_SYMBOL_GPL(xt_tee_enabled); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY ++DEFINE_PER_CPU(seqcount_t, xt_recseq); ++EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); ++ + static int xt_jumpstack_alloc(struct xt_table_info *i) + { + unsigned int size; +@@ -1536,6 +1537,7 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++#endif + + #ifdef CONFIG_PROC_FS + static void *xt_table_seq_start(struct seq_file *seq, loff_t *pos) +@@ -1919,6 +1921,7 @@ void xt_proto_fini(struct net *net, u_int8_t af) + } + EXPORT_SYMBOL_GPL(xt_proto_fini); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY + /** + * xt_percpu_counter_alloc - allocate x_tables rule counter + * +@@ -1973,6 +1976,7 @@ void xt_percpu_counter_free(struct xt_counters *counters) + free_percpu((void __percpu *)pcnt); + } + EXPORT_SYMBOL_GPL(xt_percpu_counter_free); ++#endif + + static int __net_init xt_net_init(struct net *net) + { +@@ -2005,8 +2009,10 @@ static int __init xt_init(void) + unsigned int i; + int rv; + +- for_each_possible_cpu(i) { +- seqcount_init(&per_cpu(xt_recseq, i)); ++ if (IS_ENABLED(CONFIG_NETFILTER_XTABLES_LEGACY)) { ++ for_each_possible_cpu(i) { ++ seqcount_init(&per_cpu(xt_recseq, i)); ++ } + } + + xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-5.15/netfilter-make-legacy-configs-user-selectable.patch b/queue-5.15/netfilter-make-legacy-configs-user-selectable.patch new file mode 100644 index 0000000000..51144c076d --- /dev/null +++ b/queue-5.15/netfilter-make-legacy-configs-user-selectable.patch @@ -0,0 +1,104 @@ +From 5efc00fb9fb380446b8c97f514aa19916dcee2b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Sep 2024 02:58:54 -0700 +Subject: netfilter: Make legacy configs user selectable + +From: Breno Leitao + +[ Upstream commit 6c959fd5e17387201dba3619b2e6af213939a0a7 ] + +This option makes legacy Netfilter Kconfig user selectable, giving users +the option to configure iptables without enabling any other config. + +Make the following KConfig entries user selectable: + * BRIDGE_NF_EBTABLES_LEGACY + * IP_NF_ARPTABLES + * IP_NF_IPTABLES_LEGACY + * IP6_NF_IPTABLES_LEGACY + +Signed-off-by: Breno Leitao +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 8 +++++++- + net/ipv4/netfilter/Kconfig | 16 ++++++++++++++-- + net/ipv6/netfilter/Kconfig | 9 ++++++++- + 3 files changed, 29 insertions(+), 4 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 104c0125e32e8..f16bbbbb94817 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -41,7 +41,13 @@ config NF_CONNTRACK_BRIDGE + + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY +- tristate ++ tristate "Legacy EBTABLES support" ++ depends on BRIDGE && NETFILTER_XTABLES ++ default n ++ help ++ Legacy ebtables packet/frame classifier. ++ This is not needed if you are using ebtables over nftables ++ (iptables-nft). + + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index e752a07a871fe..2e540786f9512 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -12,7 +12,13 @@ config NF_DEFRAG_IPV4 + + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP tables support" ++ default n ++ select NETFILTER_XTABLES ++ help ++ iptables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" +@@ -340,7 +346,13 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate ++ tristate "Legacy ARPTABLES support" ++ depends on NETFILTER_XTABLES ++ default n ++ help ++ arptables is a legacy packet classifier. ++ This is not needed if you are using arptables over nftables ++ (iptables-nft). + + config NFT_COMPAT_ARP + tristate +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index bc51a77fb6c07..670d23f926e62 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -8,7 +8,14 @@ menu "IPv6: Netfilter Configuration" + + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP6 tables support" ++ depends on INET && IPV6 ++ select NETFILTER_XTABLES ++ default n ++ help ++ ip6tables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" +-- +2.53.0 + diff --git a/queue-5.15/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-5.15/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..5edb6c2e55 --- /dev/null +++ b/queue-5.15/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From ae03afb432750e922150888d09a18044ba580ea6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 5897f3dbaf7c3..df2022fe440b0 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -310,6 +310,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 8d09bfe850dc3..68f0153531e64 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 79e73fd7d965c..45302640c1ca9 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(struct sk_buff *skb, + const struct nf_hook_state *state, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 564054123772a..9b905c6562313 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index aee7cd584c926..a2a267e1b2573 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1790,14 +1790,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1952,7 +1944,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 9155c5b5318d7..9dbebfa057ee8 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -68,7 +68,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index f2997709c08b1..b7322b0051a6b 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -95,7 +95,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 226000a740860..e5e30d4e37eb0 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -136,6 +136,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 4749ecc9a416d..77dabf8ff4388 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -61,7 +61,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 3e85be8cc9803..89f8f93b36f64 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -57,7 +57,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index afd22ea9f555b..4fbb6111ed56e 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1797,14 +1797,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1960,7 +1952,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 477982fcc04ae..76b5cb69a54a0 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -68,7 +68,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index bf062c01041ec..387c53da77fd6 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -88,7 +88,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 229a81cf1a729..18d5b39936466 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -138,6 +138,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 6214c0b97f123..a13a3c6298b01 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -60,7 +60,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 36b62f848897a..56057c01ff803 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -56,7 +56,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 30af321d6c964..85155c64d0443 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1537,6 +1537,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-5.15/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-5.15/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..1c96aa2ad7 --- /dev/null +++ b/queue-5.15/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From 287a51bf65c0fd8401bdacbb6faa6f63237e1d42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index df2022fe440b0..706f08839050a 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -309,8 +309,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 9b905c6562313..f9dd18244f251 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index a2a267e1b2573..1829bf3774062 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1705,12 +1705,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1719,6 +1717,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1792,7 +1791,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index e5e30d4e37eb0..d5153736f1d8c 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -126,8 +126,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 4fbb6111ed56e..2b4c3fa5a8d08 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1715,12 +1715,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1729,6 +1727,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1799,7 +1798,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 18d5b39936466..4ba85748bf6d3 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -128,8 +128,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 85155c64d0443..7c87e1a478d68 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1521,23 +1524,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1547,6 +1533,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1556,6 +1550,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1566,6 +1561,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2012,8 +2051,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2022,8 +2063,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-5.15/netfilter-x_tables-unregister-the-templates-first.patch b/queue-5.15/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..1d45345616 --- /dev/null +++ b/queue-5.15/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From ac710563c6967337787a1827e1fa9ad3bf169ce5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 3de78416ec762..771eec4629352 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -90,8 +90,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 0eb0e2ab9bfc4..9155c5b5318d7 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -108,8 +108,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 40417a3f930b2..f2997709c08b1 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -134,8 +134,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 8265c67657053..4749ecc9a416d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -108,9 +108,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index f519162a2fa51..3e85be8cc9803 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -96,9 +96,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 727ee80970124..477982fcc04ae 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -108,8 +108,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 9b518ce37d6ae..bf062c01041ec 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -127,8 +127,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 4f2a04af71d32..6214c0b97f123 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -106,8 +106,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 931674034d8be..36b62f848897a 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -95,8 +95,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-5.15/netfilter-xtables-allow-xtables-nft-only-builds.patch b/queue-5.15/netfilter-xtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..da3b1060cd --- /dev/null +++ b/queue-5.15/netfilter-xtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,315 @@ +From 3ee29420e4e1f869606efb8da60dabf7285792a2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:11 +0100 +Subject: netfilter: xtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit a9525c7f6219cee9284c0031c5930e8d41384677 ] + +Add hidden IP(6)_NF_IPTABLES_LEGACY symbol. + +When any of the "old" builtin tables are enabled the "old" iptables +interface will be supported. + +To disable the old set/getsockopt interface the existing options +for the builtin tables need to be turned off: + +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER is not set +CONFIG_IP_NF_NAT is not set +CONFIG_IP_NF_MANGLE is not set +CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_SECURITY is not set + +Same for CONFIG_IP6_NF_ variants. + +This allows to build a kernel that only supports ip(6)tables-nft +(iptables-over-nftables api). + +In the future the _LEGACY symbol will become visible and the select +statements will be turned into 'depends on', but for now be on safe side +so "make oldconfig" won't break things. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 15 ++++++++++++--- + net/ipv4/netfilter/Makefile | 2 +- + net/ipv6/netfilter/Kconfig | 20 ++++++++++++++------ + net/ipv6/netfilter/Makefile | 2 +- + net/netfilter/Kconfig | 12 ++++++------ + 5 files changed, 34 insertions(+), 17 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 5c2cdcb19dba3..7c2b8a652016d 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -10,6 +10,10 @@ config NF_DEFRAG_IPV4 + tristate + default n + ++# old sockopt interface and eval loop ++config IP_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" + help +@@ -160,7 +164,7 @@ config IP_NF_MATCH_ECN + config IP_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP_NF_MANGLE || IP_NF_RAW ++ depends on IP_NF_MANGLE || IP_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -181,6 +185,7 @@ config IP_NF_MATCH_TTL + config IP_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -190,7 +195,7 @@ config IP_NF_FILTER + + config IP_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP_NF_FILTER ++ depends on IP_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV4 + default m if NETFILTER_ADVANCED=n + help +@@ -220,6 +225,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT ++ select IP6_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -260,6 +266,7 @@ endif # IP_NF_NAT + config IP_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -283,7 +290,7 @@ config IP_NF_TARGET_CLUSTERIP + + config IP_NF_TARGET_ECN + tristate "ECN target support" +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `ECN' target, which can be used in the iptables mangle +@@ -308,6 +315,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -321,6 +329,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile +index f38fb1368ddb2..d3150ea5b8e57 100644 +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -28,7 +28,7 @@ obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o + obj-$(CONFIG_NF_FLOW_TABLE_IPV4) += nf_flow_table_ipv4.o + + # generic IP tables +-obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o ++obj-$(CONFIG_IP_NF_IPTABLES_LEGACY) += ip_tables.o + + # the three instances of ip_tables + obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index f22233e44ee97..bc51a77fb6c07 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -6,6 +6,10 @@ + menu "IPv6: Netfilter Configuration" + depends on INET && IPV6 && NETFILTER + ++# old sockopt interface and eval loop ++config IP6_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" + help +@@ -155,7 +159,7 @@ config IP6_NF_MATCH_MH + config IP6_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP6_NF_MANGLE || IP6_NF_RAW ++ depends on IP6_NF_MANGLE || IP6_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -194,6 +198,8 @@ config IP6_NF_TARGET_HL + config IP6_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY ++ tristate + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -203,7 +209,7 @@ config IP6_NF_FILTER + + config IP6_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP6_NF_FILTER ++ depends on IP6_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV6 + default m if NETFILTER_ADVANCED=n + help +@@ -229,6 +235,7 @@ config IP6_NF_TARGET_SYNPROXY + config IP6_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -238,6 +245,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -251,6 +259,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -262,6 +271,7 @@ config IP6_NF_NAT + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED + select NF_NAT ++ select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +@@ -270,25 +280,23 @@ config IP6_NF_NAT + + To compile it as a module, choose M here. If unsure, say N. + +-if IP6_NF_NAT +- + config IP6_NF_TARGET_MASQUERADE + tristate "MASQUERADE target support" + select NETFILTER_XT_TARGET_MASQUERADE ++ depends on IP6_NF_NAT + help + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects NETFILTER_XT_TARGET_MASQUERADE. + + config IP6_NF_TARGET_NPT + tristate "NPT (Network Prefix translation) target support" ++ depends on IP6_NF_NAT || NFT_COMPAT + help + This option adds the `SNPT' and `DNPT' target, which perform + stateless IPv6-to-IPv6 Network Prefix Translation per RFC 6296. + + To compile it as a module, choose M here. If unsure, say N. + +-endif # IP6_NF_NAT +- + endif # IP6_NF_IPTABLES + endmenu + +diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile +index b85383606df71..7d0a913529891 100644 +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -4,7 +4,7 @@ + # + + # Link order matters here. +-obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o ++obj-$(CONFIG_IP6_NF_IPTABLES_LEGACY) += ip6_tables.o + obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o + obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index f02ebe4609650..fdfda4b6bff67 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -812,7 +812,7 @@ config NETFILTER_XT_TARGET_AUDIT + + config NETFILTER_XT_TARGET_CHECKSUM + tristate "CHECKSUM target support" +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `CHECKSUM' target, which can be used in the iptables mangle +@@ -863,7 +863,7 @@ config NETFILTER_XT_TARGET_CONNSECMARK + config NETFILTER_XT_TARGET_CT + tristate '"CT" target support' + depends on NF_CONNTRACK +- depends on IP_NF_RAW || IP6_NF_RAW ++ depends on IP_NF_RAW || IP6_NF_RAW || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This options adds a `CT' target, which allows to specify initial +@@ -874,7 +874,7 @@ config NETFILTER_XT_TARGET_CT + + config NETFILTER_XT_TARGET_DSCP + tristate '"DSCP" and "TOS" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `DSCP' target, which allows you to manipulate +@@ -890,7 +890,7 @@ config NETFILTER_XT_TARGET_DSCP + + config NETFILTER_XT_TARGET_HL + tristate '"HL" hoplimit target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds the "HL" (for IPv6) and "TTL" (for IPv4) +@@ -1074,7 +1074,7 @@ config NETFILTER_XT_TARGET_TPROXY + depends on NETFILTER_ADVANCED + depends on IPV6 || IPV6=n + depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + select NF_DEFRAG_IPV4 + select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n + select NF_TPROXY_IPV4 +@@ -1141,7 +1141,7 @@ config NETFILTER_XT_TARGET_TCPMSS + + config NETFILTER_XT_TARGET_TCPOPTSTRIP + tristate '"TCPOPTSTRIP" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a "TCPOPTSTRIP" target, which allows you to strip +-- +2.53.0 + diff --git a/queue-5.15/netfilter-xtables-fix-up-kconfig-dependencies.patch b/queue-5.15/netfilter-xtables-fix-up-kconfig-dependencies.patch new file mode 100644 index 0000000000..7edce8a77e --- /dev/null +++ b/queue-5.15/netfilter-xtables-fix-up-kconfig-dependencies.patch @@ -0,0 +1,58 @@ +From 68db07bb9db862eff5c2e6e80b9cfbb8422819dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 Feb 2024 14:55:53 +0100 +Subject: netfilter: xtables: fix up kconfig dependencies + +From: Florian Westphal + +[ Upstream commit 749d4ef0868c5d8a98e07073791b2198178c93b4 ] + +Randy Dunlap reports arptables build failure: +arp_tables.c:(.text+0x20): undefined reference to `xt_find_table' + +... because recent change removed a 'select' on the xtables core. +Add a "depends" clause on arptables to resolve this. + +Kernel test robot reports another build breakage: +iptable_nat.c:(.text+0x8): undefined reference to `ipt_unregister_table_exit' + +... because of a typo, the nat table selected ip6tables. + +Reported-by: kernel test robot +Reported-by: Randy Dunlap +Closes: https://lore.kernel.org/netfilter-devel/d0dfbaef-046a-4c42-9daa-53636664bf6d@infradead.org/ +Fixes: a9525c7f6219 ("netfilter: xtables: allow xtables-nft only builds") +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Acked-by: Randy Dunlap +Tested-by: Randy Dunlap # build-tested +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 7c2b8a652016d..18f60e675c438 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -225,7 +225,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP6_NF_IPTABLES_LEGACY ++ select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -351,6 +351,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +-- +2.53.0 + diff --git a/queue-5.15/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-5.15/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..d86fe04d20 --- /dev/null +++ b/queue-5.15/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From 14eece03f0b740daec6c7dcbd79b480695155f79 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 8834436bc9dbc..e3a9278c06842 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-5.15/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-5.15/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..5fb3e2122d --- /dev/null +++ b/queue-5.15/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From 930a02989b297e8453692d120d6654cbff3dce9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 38693b735c876..87b7fd09a6f6f 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-5.15/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-5.15/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..2a2c1eb0b8 --- /dev/null +++ b/queue-5.15/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From 3c4c6f6c200c3cc8d1fabdc3cb376d7cb6c4169b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 62a1d93464750..eb5e533bf0866 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-5.15/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-5.15/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..4e7cfb0ea7 --- /dev/null +++ b/queue-5.15/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From 97dd382d9874ef7cd44a106946bec5a779454909 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index cbc4ec2f8479b..b19cd6ca4e2f5 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -638,12 +638,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-5.15/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-5.15/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..b52a50d883 --- /dev/null +++ b/queue-5.15/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From 6c889a6c29fe9d650be2299984fdd6e9f1e49c59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index 4e9d3f25c35d0..0906530b2bad4 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -272,12 +272,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-5.15/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-5.15/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..2ad31c384f --- /dev/null +++ b/queue-5.15/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From 79441ac01f1da4d19f7b27528b4ebcc1daa3335e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 309080184aac7..d42659a2e9058 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -294,8 +294,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 17eacf1679..cf68bd7b31 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -728,3 +728,56 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +arm-integrator-fix-early-initialization.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-arp_tables-allow-use-of-arpt_do_table-as-h.patch +netfilter-arptables-allow-xtables-nft-only-builds.patch +netfilter-xtables-allow-xtables-nft-only-builds.patch +netfilter-ebtables-allow-xtables-nft-only-builds.patch +netfilter-xtables-fix-up-kconfig-dependencies.patch +netfilter-arptables-select-netfilter_family_arp-when.patch +netfilter-make-legacy-configs-user-selectable.patch +netfilter-exclude-legacy-tables-on-preempt_rt.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +irqchip-ath79-cpu-remove-unused-function.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +net-bridge-flush-multicast-groups-when-snooping-is-d.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +net-ag71xx-check-error-for-platform_get_irq.patch +string-add-mem_is_zero-helper-to-check-if-memory-are.patch +gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch diff --git a/queue-5.15/string-add-mem_is_zero-helper-to-check-if-memory-are.patch b/queue-5.15/string-add-mem_is_zero-helper-to-check-if-memory-are.patch new file mode 100644 index 0000000000..7388dafdb1 --- /dev/null +++ b/queue-5.15/string-add-mem_is_zero-helper-to-check-if-memory-are.patch @@ -0,0 +1,51 @@ +From 63aebd04a31e98fc886733a84842ca6ed2d89e17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Aug 2024 13:00:34 +0300 +Subject: string: add mem_is_zero() helper to check if memory area is all zeros + +From: Jani Nikula + +[ Upstream commit 3942bb49728ad9e1f94d953a88af169a8f5d8099 ] + +Almost two thirds of the memchr_inv() usages check if the memory area is +all zeros, with no interest in where in the buffer the first non-zero +byte is located. Checking for !memchr_inv(s, 0, n) is also not very +intuitive or discoverable. Add an explicit mem_is_zero() helper for this +use case. + +Reviewed-by: Kees Cook +Reviewed-by: Andy Shevchenko +Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com +Signed-off-by: Jani Nikula +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + include/linux/string.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/include/linux/string.h b/include/linux/string.h +index bf368130bc42b..0e2f82182ab40 100644 +--- a/include/linux/string.h ++++ b/include/linux/string.h +@@ -212,6 +212,18 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt) + void *memchr_inv(const void *s, int c, size_t n); + char *strreplace(char *s, char old, char new); + ++/** ++ * mem_is_zero - Check if an area of memory is all 0's. ++ * @s: The memory area ++ * @n: The size of the area ++ * ++ * Return: True if the area of memory is all 0's. ++ */ ++static inline bool mem_is_zero(const void *s, size_t n) ++{ ++ return !memchr_inv(s, 0, n); ++} ++ + extern void kfree_const(const void *x); + + extern char *kstrdup(const char *s, gfp_t gfp) __malloc; +-- +2.53.0 + diff --git a/queue-5.15/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-5.15/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..2dda6c1d7c --- /dev/null +++ b/queue-5.15/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From 7710310f0bc583065521f7fb04106a3b869c2e45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index 412c06bf60362..d99fed07b024f 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -934,7 +934,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-5.15/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-5.15/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..5dbc1dd1a7 --- /dev/null +++ b/queue-5.15/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From a111460efde9116bf0c2ed01838f0156075c000e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index 03473d1e5f8bf..5ecc78916431d 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1146,10 +1146,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-5.15/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-5.15/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..17af45a138 --- /dev/null +++ b/queue-5.15/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,75 @@ +From cfc45b34b20363df43f573a7961cb1c5a226a355 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 28b4527b993fe..bacf124eec882 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7320,6 +7320,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -7333,14 +7334,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -7355,5 +7362,11 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } +-- +2.53.0 + diff --git a/queue-6.1/arm-integrator-fix-early-initialization.patch b/queue-6.1/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..b7e8b60f6e --- /dev/null +++ b/queue-6.1/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From dfb39e0e87fbdce3a245bdfb820ad961fd5ae90d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c +index 2ed4ded56b3fe..03dfb5f720b7b 100644 +--- a/arch/arm/mach-versatile/integrator_cp.c ++++ b/arch/arm/mach-versatile/integrator_cp.c +@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-6.1/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-6.1/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..c6cc869df5 --- /dev/null +++ b/queue-6.1/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From a0cc8e6e2e5f78255a348b86b8e46af3a5788cf9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index a58b85d21468e..9b54fe10d280a 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4468,10 +4468,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4479,7 +4493,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4496,7 +4509,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4504,8 +4517,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-6.1/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch b/queue-6.1/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch new file mode 100644 index 0000000000..f012ab874b --- /dev/null +++ b/queue-6.1/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch @@ -0,0 +1,50 @@ +From eb82903a66895f4ef4d2e7273d85a74aa6d70b70 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 20:21:38 +0300 +Subject: drm/msm/dsi: don't dump registers past the mapped region + +From: Dmitry Baryshkov + +[ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] + +On DSI 6G platforms the IO address space is internally adjusted by +io_offset. Later this adjusted address might be used for memory dumping. +However the size that is used for memory dumping isn't adjusted to +account for the io_offset, leading to the potential access to the +unmapped region. Lower ctrl_size by the io_offset value to prevent +access past the mapped area. + + msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) + msm_dsi_host_snapshot+0x4c/0x78 [msm] + msm_dsi_snapshot+0x28/0x50 [msm] + msm_disp_snapshot_capture_state+0x74/0x140 [msm] + msm_disp_snapshot_state_sync+0x60/0x90 [msm] + _msm_disp_snapshot_work+0x30/0x90 [msm] + kthread_worker_fn+0xdc/0x460 + kthread+0x120/0x140 + +Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Patchwork: https://patchwork.freedesktop.org/patch/721747/ +Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c +index 88843505d89c9..a2a6cb3a2262d 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_host.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_host.c +@@ -1947,6 +1947,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) + + /* fixup base address by io offset */ + msm_host->ctrl_base += cfg->io_offset; ++ msm_host->ctrl_size -= cfg->io_offset; + + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, +-- +2.53.0 + diff --git a/queue-6.1/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-6.1/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..f4e06d7dcd --- /dev/null +++ b/queue-6.1/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,51 @@ +From 77c1db3834c8db1d58625a462ea6079725d5cf91 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index 0de3612135e96..aa69713ea7f80 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -359,14 +359,15 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, size_t len, int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + /* The arm-smmu driver expects the addresses to be sign extended */ + if (iova & BIT_ULL(48)) + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.1/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-6.1/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..f4b7fbbe2f --- /dev/null +++ b/queue-6.1/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From d307e05589c08a3105cfd185eef7d87d8f62a302 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index 4d55e3cf570f0..a966a03167cc0 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -9,7 +9,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + if (!(*reg)) + *reg = kvzalloc(len_padded, GFP_KERNEL); +@@ -51,8 +51,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -61,6 +61,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -70,6 +71,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -189,7 +201,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-6.1/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-6.1/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..44d1c80e8a --- /dev/null +++ b/queue-6.1/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From 2fbb45423a4460e9766d1f8adc3798dcca3a0e10 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-6.1/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-6.1/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..e92a1c208b --- /dev/null +++ b/queue-6.1/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From 05d6a18b65047d9a27f25172088c3f28db660b4d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index 5bda5d7ade42d..4ef02f0125ab9 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -24,6 +24,8 @@ static int ffa_device_match(struct device *dev, struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -107,7 +109,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-6.1/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-6.1/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..5d8adfd204 --- /dev/null +++ b/queue-6.1/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From 8855907a2dbd76aaad80708cc34ca5f8c1641720 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e1e278d431e97..f7c72fcc9b5e3 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -816,7 +816,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.1/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-6.1/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..54528e7356 --- /dev/null +++ b/queue-6.1/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From 53a1e843a2cf9dec10f247a95e54c3182650696f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 944aa0c1cd5c7..5d4646f0baacd 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1332,6 +1332,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1339,9 +1340,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-6.1/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch b/queue-6.1/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch new file mode 100644 index 0000000000..311cffc567 --- /dev/null +++ b/queue-6.1/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch @@ -0,0 +1,70 @@ +From b198a742cdc3415a01f0d74572a279a8ae2dc9db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Nov 2024 22:16:15 +0200 +Subject: gpiolib: cdev: use !mem_is_zero() instead of memchr_inv(s, 0, n) + +From: Andy Shevchenko + +[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ] + +Use the mem_is_zero() helper where possible. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 897d20996a8c6..944aa0c1cd5c7 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -25,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1339,7 +1339,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + +- if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) ++ if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + + for (i = 0; i < num_lines; i++) { +@@ -1781,7 +1781,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) + return -EINVAL; + +- if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) ++ if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) + return -EINVAL; + + lc = &ulr.config; +@@ -2541,7 +2541,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +- if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) ++ if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) + return -EINVAL; + + desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); +-- +2.53.0 + diff --git a/queue-6.1/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-6.1/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..a09a4cd421 --- /dev/null +++ b/queue-6.1/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 80538df6baa0c6ed726af240ce09e4f60a0c122c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 99fca77d16641..91ce2b6840144 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -222,7 +222,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-6.1/hid-uclogic-fix-regression-of-input-name-assignment.patch b/queue-6.1/hid-uclogic-fix-regression-of-input-name-assignment.patch new file mode 100644 index 0000000000..7fd25518a4 --- /dev/null +++ b/queue-6.1/hid-uclogic-fix-regression-of-input-name-assignment.patch @@ -0,0 +1,44 @@ +From 5faf58ffa5858737bbc1ab7eb63d7e31637fc1e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 10:33:16 +0200 +Subject: HID: uclogic: Fix regression of input name assignment + +From: Takashi Iwai + +[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] + +The previous fix for adding the devm_kasprintf() return check in the +commit bd07f751208b ("HID: uclogic: Add NULL check in +uclogic_input_configured()") changed the condition of hi->input->name +assignment, and it resulted in missing the proper input device name +when no custom suffix is defined. + +Restore the conditional to the original content to address the +regression. + +Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") +Signed-off-by: Takashi Iwai +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-uclogic-core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c +index 5b35f9f321d41..7658bd678e7e3 100644 +--- a/drivers/hid/hid-uclogic-core.c ++++ b/drivers/hid/hid-uclogic-core.c +@@ -142,7 +142,9 @@ static int uclogic_input_configured(struct hid_device *hdev, + suffix = "System Control"; + break; + } +- } else { ++ } ++ ++ if (suffix) { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) +-- +2.53.0 + diff --git a/queue-6.1/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-6.1/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..17326809a9 --- /dev/null +++ b/queue-6.1/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From c3a95a2c98d980ce540b5daf335c4bd426269f26 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index 9aa0437aa598e..cc097207cf97f 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -530,14 +530,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + int ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-6.1/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch b/queue-6.1/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch new file mode 100644 index 0000000000..c1722ebff8 --- /dev/null +++ b/queue-6.1/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch @@ -0,0 +1,57 @@ +From 98f1089e6738a38bd28887e8d41079089b694e79 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 23:03:28 -0400 +Subject: ipv6: route: Unregister netdevice notifier on BPF init failure + +From: Yuho Choi + +[ Upstream commit 1341db322417266fb5845df81d28305b83a37324 ] + +ip6_route_init() registers ip6_route_dev_notifier before registering the +IPv6 route BPF iterator target. If bpf_iter_register() fails after the +notifier has been registered, the error path currently jumps to +out_register_late_subsys and unwinds the RTNL handlers and pernet route +state without removing the notifier from the netdevice notifier chain. + +This leaves ip6_route_dev_notify() callable after the IPv6 route state it +uses has been torn down. Add a separate unwind label for the BPF iterator +failure path and unregister the netdevice notifier before continuing with +the existing cleanup. + +Fixes: 138d0be35b14 ("net: bpf: Add netlink and ipv6_route bpf_iter targets") +Signed-off-by: Yuho Choi +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 987ef0954e2ea..2ab8aacf5513d 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6802,7 +6802,7 @@ int __init ip6_route_init(void) + #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) + ret = bpf_iter_register(); + if (ret) +- goto out_register_late_subsys; ++ goto out_register_notifier; + #endif + #endif + +@@ -6817,6 +6817,10 @@ int __init ip6_route_init(void) + out: + return ret; + ++#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) ++out_register_notifier: ++ unregister_netdevice_notifier(&ip6_route_dev_notifier); ++#endif + out_register_late_subsys: + rtnl_unregister_all(PF_INET6); + unregister_pernet_subsys(&ip6_route_net_late_ops); +-- +2.53.0 + diff --git a/queue-6.1/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch b/queue-6.1/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch new file mode 100644 index 0000000000..fe25601d2e --- /dev/null +++ b/queue-6.1/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch @@ -0,0 +1,68 @@ +From f17ce49db1bd9c14b418f2ffb8c32e549cde67e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Mar 2026 15:32:29 +0800 +Subject: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jiayuan Chen + +[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] + +On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via +run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. + +After irq_work_single() clears BUSY via atomic_cmpxchg(), it still +dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). + +An irq_work_sync() caller on another CPU that enters after BUSY is cleared +can observe BUSY==0 immediately, return, and free the work before those +accesses complete — causing a use-after-free. + +Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire +irq_work_single() execution is within an RCU read-side critical +section. Then add synchronize_rcu() in irq_work_sync() after +rcuwait_wait_event() to ensure the caller waits for the RCU grace period +before returning, preventing premature frees. + +Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") +Suggested-by: Sebastian Andrzej Siewior +Suggested-by: Steven Rostedt +Signed-off-by: Jiayuan Chen +Signed-off-by: Thomas Gleixner +Reviewed-by: Sebastian Andrzej Siewior +Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev +Signed-off-by: Sasha Levin +--- + kernel/irq_work.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/kernel/irq_work.c b/kernel/irq_work.c +index 7afa40fe5cc43..6a286b2681178 100644 +--- a/kernel/irq_work.c ++++ b/kernel/irq_work.c +@@ -282,6 +282,12 @@ void irq_work_sync(struct irq_work *work) + !arch_irq_work_has_interrupt()) { + rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), + TASK_UNINTERRUPTIBLE); ++ /* ++ * Ensure irq_work_single() does not access @work ++ * after removing IRQ_WORK_BUSY. It is always ++ * accessed within a RCU-read section. ++ */ ++ synchronize_rcu(); + return; + } + +@@ -292,6 +298,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); + + static void run_irq_workd(unsigned int cpu) + { ++ guard(rcu)(); + irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + +-- +2.53.0 + diff --git a/queue-6.1/irqchip-ath79-cpu-remove-unused-function.patch b/queue-6.1/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..4c17218dd2 --- /dev/null +++ b/queue-6.1/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From 1898b4f24f29be4b2d95632ec3dbb6112b23bdd8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-6.1/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-6.1/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..09dc89ddad --- /dev/null +++ b/queue-6.1/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From a0fd9ec456e18e4e0dadb32381e531efd8bb3e71 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 626719b95badd..785c2cfc530c2 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-6.1/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-6.1/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..d6122667c8 --- /dev/null +++ b/queue-6.1/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From 195420606b34377dc5d02cab0de2e86ee15d6f06 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 785c2cfc530c2..49defb2f21b69 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-6.1/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-6.1/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..322330f2f0 --- /dev/null +++ b/queue-6.1/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From 10cb33d1edcc19ff6d65b4b2a588929b0d47e87b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index ff93b00dcd613..c7b5b87362546 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1880,6 +1880,9 @@ static int ag71xx_probe(struct platform_device *pdev) + return -ENOMEM; + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-6.1/net-bridge-flush-multicast-groups-when-snooping-is-d.patch b/queue-6.1/net-bridge-flush-multicast-groups-when-snooping-is-d.patch new file mode 100644 index 0000000000..66c2b3776b --- /dev/null +++ b/queue-6.1/net-bridge-flush-multicast-groups-when-snooping-is-d.patch @@ -0,0 +1,67 @@ +From 1f375ce4124cfd261472d9fa93b5f4206c52bc8c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Oct 2025 16:45:37 +0200 +Subject: net: bridge: Flush multicast groups when snooping is disabled + +From: Petr Machata + +[ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] + +When forwarding multicast packets, the bridge takes MDB into account when +IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the +MDB is retained, even though it is not used anymore. + +At the same time, during the time that snooping is disabled, the IGMP / MLD +control packets are obviously ignored, and after the snooping is reenabled, +the administrator has to assume it is out of sync. In particular, missed +join and leave messages would lead to traffic being forwarded to wrong +interfaces. + +Keeping the MDB entries around thus serves no purpose, and just takes +memory. Note also that disabling per-VLAN snooping does actually flush the +relevant MDB entries. + +This patch flushes non-permanent MDB entries as global snooping is +disabled. + +Signed-off-by: Petr Machata +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 140dbcfc8b949..a58b85d21468e 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4468,6 +4468,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + ++static void br_multicast_del_grps(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_disable_port_ctx(&port->multicast_ctx); ++} ++ + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +@@ -4488,6 +4496,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; ++ br_multicast_del_grps(br); + goto unlock; + } + +-- +2.53.0 + diff --git a/queue-6.1/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-6.1/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..2d36bbb0e5 --- /dev/null +++ b/queue-6.1/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From 0b5f5fabbab2d8de4585db610e8ec779ba7658c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 308e56a73df01..3e81871688a9f 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -911,12 +911,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2381,6 +2385,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2560,6 +2566,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-6.1/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-6.1/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..4dcccc8a2f --- /dev/null +++ b/queue-6.1/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From 4b9cd544efa55adf79361969f3de983b2a0af747 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index fc3c97d0df4fc..73eeb6aeac5cc 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1187,37 +1187,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-6.1/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch b/queue-6.1/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch new file mode 100644 index 0000000000..cb147a190d --- /dev/null +++ b/queue-6.1/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch @@ -0,0 +1,185 @@ +From 2673abc7cd1f0d95b458df6754af664df850c88e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Apr 2024 10:15:11 +0300 +Subject: net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to mt753x_to_cpu_fw +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Arınç ÜNAL + +[ Upstream commit 7603a0c7d2210a253265394b50567c64fbb977e4 ] + +The mt753x_bpdu_port_fw enum is globally used for manipulating the process +of deciding the forwardable ports, specifically concerning the CPU port(s). +Therefore, rename it and the values in it to mt753x_to_cpu_fw. + +Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents. + +Signed-off-by: Arınç ÜNAL +Signed-off-by: David S. Miller +Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames") +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 44 ++++++++++------------- + drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++-------------------- + 2 files changed, 56 insertions(+), 64 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 3e81871688a9f..fc3c97d0df4fc 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1191,42 +1191,34 @@ mt753x_trap_frames(struct mt7530_priv *priv) + * VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_BPC, +- MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK | +- MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK | +- MT753X_BPDU_PORT_FW_MASK, +- MT753X_PAE_BPDU_FR | +- MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | ++ BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC1, +- MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK | +- MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK, +- MT753X_R02_BPDU_FR | +- MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | ++ R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | ++ R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC2, +- MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK | +- MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK, +- MT753X_R0E_BPDU_FR | +- MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | ++ R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | ++ R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + } + + static int +diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h +index 6441e8d7f05d9..54d806a1c2713 100644 +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -66,47 +66,47 @@ enum mt753x_id { + #define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + +-/* Registers for BPDU and PAE frame control*/ ++/* Register for BPDU and PAE frame control */ + #define MT753X_BPC 0x24 +-#define MT753X_PAE_BPDU_FR BIT(25) +-#define MT753X_PAE_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_PAE_EG_TAG(x) FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x) +-#define MT753X_PAE_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_PAE_PORT_FW(x) FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) +-#define MT753X_BPDU_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_BPDU_EG_TAG(x) FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x) +-#define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :01 and :02 MAC DA frame control */ ++#define PAE_BPDU_FR BIT(25) ++#define PAE_EG_TAG_MASK GENMASK(24, 22) ++#define PAE_EG_TAG(x) FIELD_PREP(PAE_EG_TAG_MASK, x) ++#define PAE_PORT_FW_MASK GENMASK(18, 16) ++#define PAE_PORT_FW(x) FIELD_PREP(PAE_PORT_FW_MASK, x) ++#define BPDU_EG_TAG_MASK GENMASK(8, 6) ++#define BPDU_EG_TAG(x) FIELD_PREP(BPDU_EG_TAG_MASK, x) ++#define BPDU_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */ + #define MT753X_RGAC1 0x28 +-#define MT753X_R02_BPDU_FR BIT(25) +-#define MT753X_R02_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R02_EG_TAG(x) FIELD_PREP(MT753X_R02_EG_TAG_MASK, x) +-#define MT753X_R02_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R02_PORT_FW(x) FIELD_PREP(MT753X_R02_PORT_FW_MASK, x) +-#define MT753X_R01_BPDU_FR BIT(9) +-#define MT753X_R01_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R01_EG_TAG(x) FIELD_PREP(MT753X_R01_EG_TAG_MASK, x) +-#define MT753X_R01_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :03 and :0E MAC DA frame control */ ++#define R02_BPDU_FR BIT(25) ++#define R02_EG_TAG_MASK GENMASK(24, 22) ++#define R02_EG_TAG(x) FIELD_PREP(R02_EG_TAG_MASK, x) ++#define R02_PORT_FW_MASK GENMASK(18, 16) ++#define R02_PORT_FW(x) FIELD_PREP(R02_PORT_FW_MASK, x) ++#define R01_BPDU_FR BIT(9) ++#define R01_EG_TAG_MASK GENMASK(8, 6) ++#define R01_EG_TAG(x) FIELD_PREP(R01_EG_TAG_MASK, x) ++#define R01_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */ + #define MT753X_RGAC2 0x2c +-#define MT753X_R0E_BPDU_FR BIT(25) +-#define MT753X_R0E_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R0E_EG_TAG(x) FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x) +-#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) +-#define MT753X_R03_BPDU_FR BIT(9) +-#define MT753X_R03_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R03_EG_TAG(x) FIELD_PREP(MT753X_R03_EG_TAG_MASK, x) +-#define MT753X_R03_PORT_FW_MASK GENMASK(2, 0) +- +-enum mt753x_bpdu_port_fw { +- MT753X_BPDU_FOLLOW_MFC, +- MT753X_BPDU_CPU_EXCLUDE = 4, +- MT753X_BPDU_CPU_INCLUDE = 5, +- MT753X_BPDU_CPU_ONLY = 6, +- MT753X_BPDU_DROP = 7, ++#define R0E_BPDU_FR BIT(25) ++#define R0E_EG_TAG_MASK GENMASK(24, 22) ++#define R0E_EG_TAG(x) FIELD_PREP(R0E_EG_TAG_MASK, x) ++#define R0E_PORT_FW_MASK GENMASK(18, 16) ++#define R0E_PORT_FW(x) FIELD_PREP(R0E_PORT_FW_MASK, x) ++#define R03_BPDU_FR BIT(9) ++#define R03_EG_TAG_MASK GENMASK(8, 6) ++#define R03_EG_TAG(x) FIELD_PREP(R03_EG_TAG_MASK, x) ++#define R03_PORT_FW_MASK GENMASK(2, 0) ++ ++enum mt753x_to_cpu_fw { ++ TO_CPU_FW_SYSTEM_DEFAULT, ++ TO_CPU_FW_CPU_EXCLUDE = 4, ++ TO_CPU_FW_CPU_INCLUDE = 5, ++ TO_CPU_FW_CPU_ONLY = 6, ++ TO_CPU_FW_DROP = 7, + }; + + /* Registers for address table access */ +-- +2.53.0 + diff --git a/queue-6.1/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch b/queue-6.1/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch new file mode 100644 index 0000000000..f25708e6c4 --- /dev/null +++ b/queue-6.1/net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch @@ -0,0 +1,58 @@ +From 114a8c9e6abaaa563c328a8d778fd1222b642858 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 22 Apr 2025 04:10:20 +0100 +Subject: net: dsa: mt7530: sync driver-specific behavior of MT7531 variants + +From: Daniel Golle + +[ Upstream commit 497041d763016c2e8314d2f6a329a9b77c3797ca ] + +MT7531 standalone and MMIO variants found in MT7988 and EN7581 share +most basic properties. Despite that, assisted_learning_on_cpu_port and +mtu_enforcement_ingress were only applied for MT7531 but not for MT7988 +or EN7581, causing the expected issues on MMIO devices. + +Apply both settings equally also for MT7988 and EN7581 by moving both +assignments form mt7531_setup() to mt7531_setup_common(). + +This fixes unwanted flooding of packets due to unknown unicast +during DA lookup, as well as issues with heterogenous MTU settings. + +Fixes: 7f54cc9772ce ("net: dsa: mt7530: split-off common parts from mt7531_setup") +Signed-off-by: Daniel Golle +Reviewed-by: Chester A. Unal +Link: https://patch.msgid.link/89ed7ec6d4fa0395ac53ad2809742bb1ce61ed12.1745290867.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: e824e40d0e84 ("net: dsa: mt7530: fix FDB entries not aging out with short timeout") +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 1aba0cf38630f..308e56a73df01 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2558,6 +2558,9 @@ mt7531_setup_common(struct dsa_switch *ds) + struct mt7530_priv *priv = ds->priv; + int ret, i; + ++ ds->assisted_learning_on_cpu_port = true; ++ ds->mtu_enforcement_ingress = true; ++ + mt753x_trap_frames(priv); + + /* Enable and reset MIB counters */ +@@ -2701,9 +2704,6 @@ mt7531_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- ds->assisted_learning_on_cpu_port = true; +- ds->mtu_enforcement_ingress = true; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-6.1/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..ba1316b4b3 --- /dev/null +++ b/queue-6.1/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 226b1597ab6e1936a97dc53e06b698b9d0000d4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 51108bf65845d..3f0e63c7342bd 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1449,6 +1450,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1462,7 +1464,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1502,6 +1503,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1512,6 +1514,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1546,6 +1549,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1554,6 +1558,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1563,6 +1568,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1892,6 +1898,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.1/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-6.1/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..4eccdf9469 --- /dev/null +++ b/queue-6.1/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From 4af4c8a07ac94213807e6e896c6bfa20fa6f21aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index dbfcbdb8d751a..51108bf65845d 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1498,6 +1498,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-6.1/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-6.1/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..fab81b0ee2 --- /dev/null +++ b/queue-6.1/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From fbd195ed75826e57f330ccbc5e88eaecc1088807 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 729a69007ec47..dbfcbdb8d751a 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -121,6 +121,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1447,10 +1449,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1504,6 +1506,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1554,6 +1557,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1882,6 +1886,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.1/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-6.1/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..e5dcaaf522 --- /dev/null +++ b/queue-6.1/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From 5fcd86ece88e0b0578aac698823074be2643af97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index 06a0c00af99c7..75ab9b9668172 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1270,7 +1270,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1297,7 +1296,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-6.1/net-lan966x-avoid-unregistering-netdev-on-register-f.patch b/queue-6.1/net-lan966x-avoid-unregistering-netdev-on-register-f.patch new file mode 100644 index 0000000000..9befd755e1 --- /dev/null +++ b/queue-6.1/net-lan966x-avoid-unregistering-netdev-on-register-f.patch @@ -0,0 +1,65 @@ +From c3540d0cc761b7019288bf7d0c94bc64d81f8b28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 21:43:11 +0900 +Subject: net: lan966x: avoid unregistering netdev on register failure + +From: Myeonghun Pak + +[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] + +lan966x_probe_port() stores the newly allocated net_device in the +port before calling register_netdev(). If register_netdev() fails, +the probe error path calls lan966x_cleanup_ports(), which sees +port->dev and calls unregister_netdev() for a device that was never +registered. + +Destroy the phylink instance created for this port and clear port->dev +before returning the registration error. The common cleanup path now skips +ports without port->dev before reaching the registered netdev cleanup, so +it only handles ports that reached the registered-netdev lifetime. + +This also avoids treating an uninitialized FDMA netdev and the failed port +as a NULL == NULL match in the common cleanup path. + +Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +index 8c048ffde23d6..8347def40f9d4 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +@@ -688,11 +688,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; +- if (!port) ++ if (!port || !port->dev) + continue; + +- if (port->dev) +- unregister_netdev(port->dev); ++ unregister_netdev(port->dev); + + if (lan966x->fdma && lan966x->fdma_ndev == port->dev) + lan966x_fdma_netdev_deinit(lan966x, port->dev); +@@ -805,6 +804,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, + err = register_netdev(dev); + if (err) { + dev_err(lan966x->dev, "register_netdev failed\n"); ++ phylink_destroy(phylink); ++ port->phylink = NULL; ++ port->dev = NULL; + return err; + } + +-- +2.53.0 + diff --git a/queue-6.1/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-6.1/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..e059387596 --- /dev/null +++ b/queue-6.1/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 3bb5f55c0a6e67725f2688baf419763e864e8105 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 8111f181f9572..d429ecdbc5d4f 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -75,21 +75,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -200,6 +198,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -218,13 +217,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-6.1/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-6.1/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..64a97b9568 --- /dev/null +++ b/queue-6.1/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From dab1b251ee1c1123d7a61deb0bf06887fb83ebf0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index d429ecdbc5d4f..e21f10e40d188 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -214,6 +214,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-6.1/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch b/queue-6.1/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch new file mode 100644 index 0000000000..9b6577eddc --- /dev/null +++ b/queue-6.1/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch @@ -0,0 +1,65 @@ +From 3f7333267feecc9ae39d4391f6c2e0c762a773e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 15:26:40 -0700 +Subject: net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint + +From: Xiang Mei + +[ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] + +The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and +smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: + + __string(name, smc->conn.lnk->ibname) + +conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on +these paths already handles this (e.g. !conn->lnk in +SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first +sendmsg()/recvmsg() on an SMC-D socket crashes: + + Oops: general protection fault, probably for non-canonical address + KASAN: null-ptr-deref in range [...] + RIP: 0010:strlen+0x1e/0xa0 + Call Trace: + trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) + smc_rx_recvmsg (net/smc/smc_rx.c:515) + smc_recvmsg (net/smc/af_smc.c:2859) + __sys_recvfrom (net/socket.c:2315) + __x64_sys_recvfrom (net/socket.c:2326) + do_syscall_64 + +The faulting address 0x3e0 is offsetof(struct smc_link, ibname), +confirming the NULL ->lnk deref. Enabling the tracepoint requires +root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has +no capability check, and SMC-D negotiation needs no admin step on +s390 or on x86 with the loopback ISM device loaded. + +Log an empty device name for SMC-D instead of dereferencing NULL. + +Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Dust Li +Reviewed-by: Sidraya Jayagond +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/smc/smc_tracepoint.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h +index 9fc5e586d24ab..380451912c4f1 100644 +--- a/net/smc/smc_tracepoint.h ++++ b/net/smc/smc_tracepoint.h +@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, + __field(const void *, smc) + __field(u64, net_cookie) + __field(size_t, len) +- __string(name, smc->conn.lnk->ibname) ++ __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") + ), + + TP_fast_assign( +-- +2.53.0 + diff --git a/queue-6.1/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch b/queue-6.1/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch new file mode 100644 index 0000000000..2a2e6f3b6e --- /dev/null +++ b/queue-6.1/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch @@ -0,0 +1,63 @@ +From 78003da406a337a628fa4db252ece25c67eeb409 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:21:38 -0700 +Subject: net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot + +From: Xiang Mei + +[ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] + +On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is +reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() +populates V2 entries starting at index 1, so when no V1 device is +selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == +NULL and ism_chid[0] == 0. + +smc_v2_determine_accepted_chid() then matches the peer's CHID against +the array starting from index 0 using the CHID alone. A malicious +peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches +the empty slot, ini->ism_selected becomes 0, and the subsequent +ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at +offsetof(struct smcd_dev, lgr_lock) == 0x68: + + BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 + Write of size 4 at addr 0000000000000068 by task exploit/144 + Call Trace: + _raw_spin_lock_bh + smc_conn_create (net/smc/smc_core.c:1997) + __smc_connect (net/smc/af_smc.c:1447) + smc_connect (net/smc/af_smc.c:1720) + __sys_connect + __x64_sys_connect + do_syscall_64 + +Require ism_dev[i] to be non-NULL before accepting a CHID match. + +Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") +Reported-by: Weiming Shi +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/smc/af_smc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c +index a609b220b215d..b0f8eca077b89 100644 +--- a/net/smc/af_smc.c ++++ b/net/smc/af_smc.c +@@ -1346,7 +1346,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm_v2 *aclc, + int i; + + for (i = 0; i < ini->ism_offered_cnt + 1; i++) { +- if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ++ if (ini->ism_dev[i] && ++ ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + ini->ism_selected = i; + return 0; + } +-- +2.53.0 + diff --git a/queue-6.1/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-6.1/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..7cfd3b7f24 --- /dev/null +++ b/queue-6.1/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From 91ae169f2f2dbe91a105dd671b5f904330a4cb9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index ef7dda0915d33..a46f3cc4b3f14 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -801,11 +801,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-6.1/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-6.1/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..ffd22a5afa --- /dev/null +++ b/queue-6.1/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From e3d447b665c5196b4e4fa63f4bc0c536e48bf9fe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index a46f3cc4b3f14..de85e26d9675d 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -790,21 +790,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-arptables-allow-xtables-nft-only-builds.patch b/queue-6.1/netfilter-arptables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..c1bc3fa98f --- /dev/null +++ b/queue-6.1/netfilter-arptables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,82 @@ +From 5445ac1e1d917b8e8c4bca0c138dbf70539618f4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 23 Jan 2024 16:42:48 +0100 +Subject: netfilter: arptables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 4654467dc7e111e84f43ed1b70322873ae77e7be ] + +Allows to build kernel that supports the arptables mangle target +via nftables' compat infra but without the arptables get/setsockopt +interface or the old arptables filter interpreter. + +IOW, setting IP_NF_ARPFILTER=n will break arptables-legacy, but +arptables-nft will continue to work as long as nftables compat +support is enabled. + +Signed-off-by: Florian Westphal +Reviewed-by: Phil Sutter +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index aab384126f61f..483778f379d44 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -323,36 +323,34 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate "ARP tables support" +- select NETFILTER_XTABLES +- select NETFILTER_FAMILY_ARP +- depends on NETFILTER_ADVANCED +- help +- arptables is a general, extensible packet identification framework. +- The ARP packet filtering and mangling (manipulation)subsystems +- use this: say Y or M here if you want to use either of those. +- +- To compile it as a module, choose M here. If unsure, say N. ++ tristate + +-if IP_NF_ARPTABLES ++config NFT_COMPAT_ARP ++ tristate ++ depends on NF_TABLES_ARP && NFT_COMPAT ++ default m if NFT_COMPAT=m ++ default y if NFT_COMPAT=y + + config IP_NF_ARPFILTER +- tristate "ARP packet filtering" ++ tristate "arptables-legacy packet filtering support" ++ select IP_NF_ARPTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +- local output. On a bridge, you can also specify filtering rules +- for forwarded ARP packets. See the man page for arptables(8). ++ local output. This is only needed for arptables-legacy(8). ++ Neither arptables-nft nor nftables need this to work. + + To compile it as a module, choose M here. If unsure, say N. + + config IP_NF_ARP_MANGLE + tristate "ARP payload mangling" ++ depends on IP_NF_ARPTABLES || NFT_COMPAT_ARP + help + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + +-endif # IP_NF_ARPTABLES ++ This option is needed by both arptables-legacy and arptables-nft. ++ It is not used by nftables. + + endmenu + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-arptables-select-netfilter_family_arp-when.patch b/queue-6.1/netfilter-arptables-select-netfilter_family_arp-when.patch new file mode 100644 index 0000000000..65a7c6db2f --- /dev/null +++ b/queue-6.1/netfilter-arptables-select-netfilter_family_arp-when.patch @@ -0,0 +1,112 @@ +From e2b622c702013ca0d80dfd33ec3283eec78d8b63 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Mar 2024 21:15:52 -0700 +Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building + arp_tables.c + +From: Kuniyuki Iwashima + +[ Upstream commit 15fba562f7a9f04322b8bfc8f392e04bb93d81be ] + +syzkaller started to report a warning below [0] after consuming the +commit 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only +builds"). + +The change accidentally removed the dependency on NETFILTER_FAMILY_ARP +from IP_NF_ARPTABLES. + +If NF_TABLES_ARP is not enabled on Kconfig, NETFILTER_FAMILY_ARP will +be removed and some code necessary for arptables will not be compiled. + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + CONFIG_NETFILTER_FAMILY_ARP=y + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + + $ make olddefconfig + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + +So, when nf_register_net_hooks() is called for arptables, it will +trigger the splat below. + +Now IP_NF_ARPTABLES is only enabled by IP_NF_ARPFILTER, so let's +restore the dependency on NETFILTER_FAMILY_ARP in IP_NF_ARPFILTER. + +[0]: +WARNING: CPU: 0 PID: 242 at net/netfilter/core.c:316 nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Modules linked in: +CPU: 0 PID: 242 Comm: syz-executor.0 Not tainted 6.8.0-12821-g537c2e91d354 #10 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 +RIP: 0010:nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Code: 83 fd 04 0f 87 bc 00 00 00 e8 5b 84 83 fd 4d 8d ac ec a8 0b 00 00 e8 4e 84 83 fd 4c 89 e8 5b 5d 41 5c 41 5d c3 e8 3f 84 83 fd <0f> 0b e8 38 84 83 fd 45 31 ed 5b 5d 4c 89 e8 41 5c 41 5d c3 e8 26 +RSP: 0018:ffffc90000b8f6e8 EFLAGS: 00010293 +RAX: 0000000000000000 RBX: 0000000000000003 RCX: ffffffff83c42164 +RDX: ffff888106851180 RSI: ffffffff83c42321 RDI: 0000000000000005 +RBP: 0000000000000000 R08: 0000000000000005 R09: 000000000000000a +R10: 0000000000000003 R11: ffff8881055c2f00 R12: ffff888112b78000 +R13: 0000000000000000 R14: ffff8881055c2f00 R15: ffff8881055c2f00 +FS: 00007f377bd78800(0000) GS:ffff88811b000000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 0000000000496068 CR3: 000000011298b003 CR4: 0000000000770ef0 +PKRU: 55555554 +Call Trace: + + __nf_register_net_hook+0xcd/0x7a0 net/netfilter/core.c:428 + nf_register_net_hook+0x116/0x170 net/netfilter/core.c:578 + nf_register_net_hooks+0x5d/0xc0 net/netfilter/core.c:594 + arpt_register_table+0x250/0x420 net/ipv4/netfilter/arp_tables.c:1553 + arptable_filter_table_init+0x41/0x60 net/ipv4/netfilter/arptable_filter.c:39 + xt_find_table_lock+0x2e9/0x4b0 net/netfilter/x_tables.c:1260 + xt_request_find_table_lock+0x2b/0xe0 net/netfilter/x_tables.c:1285 + get_info+0x169/0x5c0 net/ipv4/netfilter/arp_tables.c:808 + do_arpt_get_ctl+0x3f9/0x830 net/ipv4/netfilter/arp_tables.c:1444 + nf_getsockopt+0x76/0xd0 net/netfilter/nf_sockopt.c:116 + ip_getsockopt+0x17d/0x1c0 net/ipv4/ip_sockglue.c:1777 + tcp_getsockopt+0x99/0x100 net/ipv4/tcp.c:4373 + do_sock_getsockopt+0x279/0x360 net/socket.c:2373 + __sys_getsockopt+0x115/0x1e0 net/socket.c:2402 + __do_sys_getsockopt net/socket.c:2412 [inline] + __se_sys_getsockopt net/socket.c:2409 [inline] + __x64_sys_getsockopt+0xbd/0x150 net/socket.c:2409 + do_syscall_x64 arch/x86/entry/common.c:52 [inline] + do_syscall_64+0x4f/0x110 arch/x86/entry/common.c:83 + entry_SYSCALL_64_after_hwframe+0x46/0x4e +RIP: 0033:0x7f377beca6fe +Code: 1f 44 00 00 48 8b 15 01 97 0a 00 f7 d8 64 89 02 b8 ff ff ff ff eb b8 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 37 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 c9 +RSP: 002b:00000000005df728 EFLAGS: 00000246 ORIG_RAX: 0000000000000037 +RAX: ffffffffffffffda RBX: 00000000004966e0 RCX: 00007f377beca6fe +RDX: 0000000000000060 RSI: 0000000000000000 RDI: 0000000000000003 +RBP: 000000000042938a R08: 00000000005df73c R09: 00000000005df800 +R10: 00000000004966e8 R11: 0000000000000246 R12: 0000000000000003 +R13: 0000000000496068 R14: 0000000000000003 R15: 00000000004bc9d8 + + +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Reported-by: syzkaller +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Simon Horman +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 0f60a740d117d..6146ef5fc728f 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -343,6 +343,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ select NETFILTER_FAMILY_ARP + depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of +-- +2.53.0 + diff --git a/queue-6.1/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-6.1/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..df46323965 --- /dev/null +++ b/queue-6.1/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From d19344d6078be196205ad30274c76ac47c05283f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index ec286e54229b7..ca426e49ea1a1 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-ebtables-allow-xtables-nft-only-builds.patch b/queue-6.1/netfilter-ebtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..2574503338 --- /dev/null +++ b/queue-6.1/netfilter-ebtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,84 @@ +From 31b08e7485e3b49baf686a56324396a6debd1582 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:12 +0100 +Subject: netfilter: ebtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 7ad269787b6615ca56bb161063331991fce51abf ] + +Same patch as previous one, but for ebtables. + +To build a kernel that only supports ebtables-nft, the builtin tables +need to be disabled, i.e.: + +CONFIG_BRIDGE_EBT_BROUTE=n +CONFIG_BRIDGE_EBT_T_FILTER=n +CONFIG_BRIDGE_EBT_T_NAT=n + +The ebtables specific extensions can then be used nftables' +NFT_COMPAT interface. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 7 +++++++ + net/bridge/netfilter/Makefile | 2 +- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 7f304a19ac1bf..104c0125e32e8 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -39,6 +39,10 @@ config NF_CONNTRACK_BRIDGE + + To compile it as a module, choose M here. If unsure, say N. + ++# old sockopt interface and eval loop ++config BRIDGE_NF_EBTABLES_LEGACY ++ tristate ++ + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" + depends on BRIDGE && NETFILTER && NETFILTER_XTABLES +@@ -55,6 +59,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -65,6 +70,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -74,6 +80,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile +index 1c9ce49ab6513..b9a1303da9771 100644 +--- a/net/bridge/netfilter/Makefile ++++ b/net/bridge/netfilter/Makefile +@@ -9,7 +9,7 @@ obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o + # connection tracking + obj-$(CONFIG_NF_CONNTRACK_BRIDGE) += nf_conntrack_bridge.o + +-obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o ++obj-$(CONFIG_BRIDGE_NF_EBTABLES_LEGACY) += ebtables.o + + # tables + obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o +-- +2.53.0 + diff --git a/queue-6.1/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-6.1/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..956d6bf1d1 --- /dev/null +++ b/queue-6.1/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From 4cabc0abf23050af4a2eb00a4347489cc6ea2c3f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 33d8640d21ac1..43c808e525e87 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index fdb988c24916a..f76d45dfe9b46 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 8b981b2041b5d..af0732e2f889d 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-6.1/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-6.1/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..894b42c2cf --- /dev/null +++ b/queue-6.1/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From 332bde2e4549c55bae7b469a718babdc55c54e13 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 8f19253024b0a..33d8640d21ac1 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 278f324e67524..fdb988c24916a 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 9066f7f376d57..8b981b2041b5d 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index f99e348c8f37f..ec286e54229b7 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-6.1/netfilter-exclude-legacy-tables-on-preempt_rt.patch b/queue-6.1/netfilter-exclude-legacy-tables-on-preempt_rt.patch new file mode 100644 index 0000000000..0379e3d967 --- /dev/null +++ b/queue-6.1/netfilter-exclude-legacy-tables-on-preempt_rt.patch @@ -0,0 +1,335 @@ +From 4fd8ba08d22ba9f7b09e67331fa5c68365b38589 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Jun 2025 17:44:23 +0200 +Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT. + +From: Pablo Neira Ayuso + +[ Upstream commit 9fce66583f06c212e95e4b76dd61d8432ffa56b6 ] + +The seqcount xt_recseq is used to synchronize the replacement of +xt_table::private in xt_replace_table() against all readers such as +ipt_do_table() + +To ensure that there is only one writer, the writing side disables +bottom halves. The sequence counter can be acquired recursively. Only the +first invocation modifies the sequence counter (signaling that a writer +is in progress) while the following (recursive) writer does not modify +the counter. +The lack of a proper locking mechanism for the sequence counter can lead +to live lock on PREEMPT_RT if the high prior reader preempts the +writer. Additionally if the per-CPU lock on PREEMPT_RT is removed from +local_bh_disable() then there is no synchronisation for the per-CPU +sequence counter. + +The affected code is "just" the legacy netfilter code which is replaced +by "netfilter tables". That code can be disabled without sacrificing +functionality because everything is provided by the newer +implementation. This will only requires the usage of the "-nft" tools +instead of the "-legacy" ones. +The long term plan is to remove the legacy code so lets accelerate the +progress. + +Relax dependencies on iptables legacy, replace select with depends on, +this should cause no harm to existing kernel configs and users can still +toggle IP{6}_NF_IPTABLES_LEGACY in any case. +Make EBTABLES_LEGACY, IPTABLES_LEGACY and ARPTABLES depend on +NETFILTER_XTABLES_LEGACY. Hide xt_recseq and its users, +xt_register_table() and xt_percpu_counter_alloc() behind +NETFILTER_XTABLES_LEGACY. Let NETFILTER_XTABLES_LEGACY depend on +!PREEMPT_RT. + +This will break selftest expecing the legacy options enabled and will be +addressed in a following patch. + +Co-developed-by: Florian Westphal +Co-developed-by: Sebastian Andrzej Siewior +Signed-off-by: Sebastian Andrzej Siewior +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 10 +++++----- + net/ipv4/netfilter/Kconfig | 24 ++++++++++++------------ + net/ipv6/netfilter/Kconfig | 19 +++++++++---------- + net/netfilter/Kconfig | 10 ++++++++++ + net/netfilter/x_tables.c | 16 +++++++++++----- + 5 files changed, 47 insertions(+), 32 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index f16bbbbb94817..60f28e4fb5c0a 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -42,8 +42,8 @@ config NF_CONNTRACK_BRIDGE + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY + tristate "Legacy EBTABLES support" +- depends on BRIDGE && NETFILTER_XTABLES +- default n ++ depends on BRIDGE && NETFILTER_XTABLES_LEGACY ++ default n + help + Legacy ebtables packet/frame classifier. + This is not needed if you are using ebtables over nftables +@@ -65,7 +65,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -76,7 +76,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -86,7 +86,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 1d0a89a67acf5..ffb1f193a8bd5 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -13,8 +13,8 @@ config NF_DEFRAG_IPV4 + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY + tristate "Legacy IP tables support" +- default n +- select NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + iptables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -182,8 +182,8 @@ config IP_NF_MATCH_TTL + # `filter', generic and specific targets + config IP_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -220,10 +220,10 @@ config IP_NF_TARGET_SYNPROXY + config IP_NF_NAT + tristate "iptables NAT support" + depends on NF_CONNTRACK ++ depends on IP_NF_IPTABLES_LEGACY + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -263,8 +263,8 @@ endif # IP_NF_NAT + # mangle + specific targets + config IP_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -313,7 +313,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -327,7 +327,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -339,8 +339,8 @@ endif # IP_NF_IPTABLES + # ARP tables + config IP_NF_ARPTABLES + tristate "Legacy ARPTABLES support" +- depends on NETFILTER_XTABLES +- default n ++ depends on NETFILTER_XTABLES_LEGACY ++ default n + help + arptables is a legacy packet classifier. + This is not needed if you are using arptables over nftables +@@ -356,7 +356,7 @@ config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES + select NETFILTER_FAMILY_ARP +- depends on NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index e087a8e97ba78..276860f65baae 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -9,9 +9,8 @@ menu "IPv6: Netfilter Configuration" + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY + tristate "Legacy IP6 tables support" +- depends on INET && IPV6 +- select NETFILTER_XTABLES +- default n ++ depends on INET && IPV6 && NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + ip6tables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -196,8 +195,8 @@ config IP6_NF_TARGET_HL + + config IP6_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + tristate + help + Packet filtering defines a table `filter', which has a series of +@@ -233,8 +232,8 @@ config IP6_NF_TARGET_SYNPROXY + + config IP6_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -244,7 +243,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -258,7 +257,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -269,8 +268,8 @@ config IP6_NF_NAT + tristate "ip6tables NAT support" + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED ++ depends on IP6_NF_IPTABLES_LEGACY + select NF_NAT +- select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index 344c287aa3f41..4937f32bcd6e7 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -760,6 +760,16 @@ config NETFILTER_XTABLES_COMPAT + + If unsure, say N. + ++config NETFILTER_XTABLES_LEGACY ++ bool "Netfilter legacy tables support" ++ depends on !PREEMPT_RT ++ help ++ Say Y here if you still require support for legacy tables. This is ++ required by the legacy tools (iptables-legacy) and is not needed if ++ you use iptables over nftables (iptables-nft). ++ Legacy support is not limited to IP, it also includes EBTABLES and ++ ARPTABLES. ++ + comment "Xtables combined modules" + + config NETFILTER_XT_MARK +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 650cb725ba271..be786cd704508 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1339,12 +1339,13 @@ void xt_compat_unlock(u_int8_t af) + EXPORT_SYMBOL_GPL(xt_compat_unlock); + #endif + +-DEFINE_PER_CPU(seqcount_t, xt_recseq); +-EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); +- + struct static_key xt_tee_enabled __read_mostly; + EXPORT_SYMBOL_GPL(xt_tee_enabled); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY ++DEFINE_PER_CPU(seqcount_t, xt_recseq); ++EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); ++ + static int xt_jumpstack_alloc(struct xt_table_info *i) + { + unsigned int size; +@@ -1536,6 +1537,7 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++#endif + + #ifdef CONFIG_PROC_FS + static void *xt_table_seq_start(struct seq_file *seq, loff_t *pos) +@@ -1919,6 +1921,7 @@ void xt_proto_fini(struct net *net, u_int8_t af) + } + EXPORT_SYMBOL_GPL(xt_proto_fini); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY + /** + * xt_percpu_counter_alloc - allocate x_tables rule counter + * +@@ -1973,6 +1976,7 @@ void xt_percpu_counter_free(struct xt_counters *counters) + free_percpu((void __percpu *)pcnt); + } + EXPORT_SYMBOL_GPL(xt_percpu_counter_free); ++#endif + + static int __net_init xt_net_init(struct net *net) + { +@@ -2005,8 +2009,10 @@ static int __init xt_init(void) + unsigned int i; + int rv; + +- for_each_possible_cpu(i) { +- seqcount_init(&per_cpu(xt_recseq, i)); ++ if (IS_ENABLED(CONFIG_NETFILTER_XTABLES_LEGACY)) { ++ for_each_possible_cpu(i) { ++ seqcount_init(&per_cpu(xt_recseq, i)); ++ } + } + + xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.1/netfilter-make-legacy-configs-user-selectable.patch b/queue-6.1/netfilter-make-legacy-configs-user-selectable.patch new file mode 100644 index 0000000000..0bd50f869f --- /dev/null +++ b/queue-6.1/netfilter-make-legacy-configs-user-selectable.patch @@ -0,0 +1,104 @@ +From b2399c7be634fdaa657a6398be0b0c86b1bdde0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Sep 2024 02:58:54 -0700 +Subject: netfilter: Make legacy configs user selectable + +From: Breno Leitao + +[ Upstream commit 6c959fd5e17387201dba3619b2e6af213939a0a7 ] + +This option makes legacy Netfilter Kconfig user selectable, giving users +the option to configure iptables without enabling any other config. + +Make the following KConfig entries user selectable: + * BRIDGE_NF_EBTABLES_LEGACY + * IP_NF_ARPTABLES + * IP_NF_IPTABLES_LEGACY + * IP6_NF_IPTABLES_LEGACY + +Signed-off-by: Breno Leitao +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 8 +++++++- + net/ipv4/netfilter/Kconfig | 16 ++++++++++++++-- + net/ipv6/netfilter/Kconfig | 9 ++++++++- + 3 files changed, 29 insertions(+), 4 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 104c0125e32e8..f16bbbbb94817 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -41,7 +41,13 @@ config NF_CONNTRACK_BRIDGE + + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY +- tristate ++ tristate "Legacy EBTABLES support" ++ depends on BRIDGE && NETFILTER_XTABLES ++ default n ++ help ++ Legacy ebtables packet/frame classifier. ++ This is not needed if you are using ebtables over nftables ++ (iptables-nft). + + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 6146ef5fc728f..1d0a89a67acf5 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -12,7 +12,13 @@ config NF_DEFRAG_IPV4 + + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP tables support" ++ default n ++ select NETFILTER_XTABLES ++ help ++ iptables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" +@@ -332,7 +338,13 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate ++ tristate "Legacy ARPTABLES support" ++ depends on NETFILTER_XTABLES ++ default n ++ help ++ arptables is a legacy packet classifier. ++ This is not needed if you are using arptables over nftables ++ (iptables-nft). + + config NFT_COMPAT_ARP + tristate +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index f3c8e2d918e13..e087a8e97ba78 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -8,7 +8,14 @@ menu "IPv6: Netfilter Configuration" + + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP6 tables support" ++ depends on INET && IPV6 ++ select NETFILTER_XTABLES ++ default n ++ help ++ ip6tables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" +-- +2.53.0 + diff --git a/queue-6.1/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-6.1/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..2ad8de5e47 --- /dev/null +++ b/queue-6.1/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From 52fa5ccd0b1247175261438baa604793b079444f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 5897f3dbaf7c3..df2022fe440b0 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -310,6 +310,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 132b0e4a6d4df..13593391d6058 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 8b8885a73c764..c6d5b927830dd 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 564054123772a..9b905c6562313 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index a6208efcfccfc..7c6b21f8174a3 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1791,14 +1791,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1953,7 +1945,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index c03c1a4ea7cab..fb85745793ba5 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 6a51e61b35562..6259bcf178bba 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -95,7 +95,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 12ca666d6e2c1..ca6964b957ead 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 33330e13ea18d..c7b91b2042dc6 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 2b89adc1e5751..81175c20ccbe8 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index b844e519da1b4..1324413fb29c3 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1797,14 +1797,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1960,7 +1952,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 16a38d56b2e54..982900920e730 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 39f0716667131..475361aa81310 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -88,7 +88,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 52d597b16b658..bef2d309369bc 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 01def8aa7a2e8..a99879f173b4a 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 66018b169b010..c44834d93fc79 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index be786cd704508..6a4bca66a0ae6 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1537,6 +1537,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-6.1/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-6.1/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..22ce933704 --- /dev/null +++ b/queue-6.1/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From 850ceb93c8b61020afb1780cf3ae25b0b52193d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index df2022fe440b0..706f08839050a 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -309,8 +309,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 9b905c6562313..f9dd18244f251 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 7c6b21f8174a3..0ff9b7c9dc59c 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1706,12 +1706,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1720,6 +1718,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1793,7 +1792,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index ca6964b957ead..87d934b12bcb6 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 1324413fb29c3..baa1c094faf48 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1715,12 +1715,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1729,6 +1727,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1799,7 +1798,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index bef2d309369bc..cf260d8ebdb70 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 6a4bca66a0ae6..cba2b8d2f9069 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1521,23 +1524,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1547,6 +1533,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1556,6 +1550,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1566,6 +1561,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2012,8 +2051,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2022,8 +2063,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-6.1/netfilter-x_tables-close-dangling-table-module-init-.patch b/queue-6.1/netfilter-x_tables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..ae22cca254 --- /dev/null +++ b/queue-6.1/netfilter-x_tables-close-dangling-table-module-init-.patch @@ -0,0 +1,406 @@ +From 4f5d0768119e450f16a20a8836c38e4fd6369d98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:20 +0200 +Subject: netfilter: x_tables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] + +Similar to the previous ebtables patch: +template add exposes the table to userspace, we must do this last to +rnsure the pernet ops are set up (contain the destructors). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ + net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- + net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- + net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- + net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- + 9 files changed, 105 insertions(+), 99 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 382345567a600..370b635e3523b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { + + static int __init arptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- arptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); +- if (IS_ERR(arpfilter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(arpfilter_ops)) + return PTR_ERR(arpfilter_ops); +- } + + ret = register_pernet_subsys(&arptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ arptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(arpfilter_ops); +- return ret; ++ unregister_pernet_subsys(&arptable_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(arpfilter_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index fb85745793ba5..409e96c72164b 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { + + static int __init iptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- iptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&iptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ iptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_filter_net_ops); ++ goto err_free; + } + + return 0; ++err_free: ++ kfree(filter_ops); ++ return ret; + } + + static void __exit iptable_filter_fini(void) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 6259bcf178bba..b8618bdf5fdc4 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -110,25 +110,26 @@ static struct pernet_operations iptable_mangle_net_ops = { + + static int __init iptable_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- iptable_mangle_table_init); +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); +- ret = PTR_ERR(mangle_ops); +- return ret; +- } ++ if (IS_ERR(mangle_ops)) ++ return PTR_ERR(mangle_ops); + + ret = register_pernet_subsys(&iptable_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ iptable_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index c7b91b2042dc6..94ad7fad3a1f3 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, +- iptable_raw_table_init); +- if (ret < 0) +- return ret; +- + rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&iptable_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ++ iptable_raw_table_init); + if (ret < 0) { +- xt_unregister_template(table); +- kfree(rawtable_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 81175c20ccbe8..491894511c544 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { + + static int __init iptable_security_init(void) + { +- int ret = xt_register_template(&security_table, +- iptable_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&iptable_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ iptable_security_table_init); + if (ret < 0) { +- xt_unregister_template(&security_table); +- kfree(sectbl_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 982900920e730..f444071346859 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { + + static int __init ip6table_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- ip6table_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&ip6table_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ip6table_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(filter_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 475361aa81310..dbc64e4428403 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -103,25 +103,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { + + static int __init ip6table_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- ip6table_mangle_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); ++ if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); +- } + + ret = register_pernet_subsys(&ip6table_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ ip6table_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index a99879f173b4a..1eadf553c746e 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, ip6table_raw_table_init); +- if (ret < 0) +- return ret; +- + /* Register hooks */ + rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&ip6table_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ip6table_raw_table_init); + if (ret < 0) { +- kfree(rawtable_ops); +- xt_unregister_template(table); +- return ret; ++ unregister_pernet_subsys(&ip6table_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index c44834d93fc79..4bd5d97b8ab65 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { + + static int __init ip6table_security_init(void) + { +- int ret = xt_register_template(&security_table, +- ip6table_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&ip6table_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ ip6table_security_table_init); + if (ret < 0) { +- kfree(sectbl_ops); +- xt_unregister_template(&security_table); +- return ret; ++ unregister_pernet_subsys(&ip6table_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-x_tables-unregister-the-templates-first.patch b/queue-6.1/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..0f11c7b96a --- /dev/null +++ b/queue-6.1/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From 2319b33ffcf04844bcf0dfc375fca7ab689b0e6c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 78cd5ee24448f..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index b9062f4552ace..c03c1a4ea7cab 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 3abb430af9e6f..6a51e61b35562 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -134,8 +134,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index ca5e5b21587cd..33330e13ea18d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index d885443cb2679..2b89adc1e5751 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index df785ebda0ca4..16a38d56b2e54 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index a88b2ce4a3cb8..39f0716667131 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -127,8 +127,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 08861d5d1f4db..01def8aa7a2e8 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 4df14a9bae782..66018b169b010 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-xtables-allow-xtables-nft-only-builds.patch b/queue-6.1/netfilter-xtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..aa25312aba --- /dev/null +++ b/queue-6.1/netfilter-xtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,315 @@ +From 1618cdd6bf14d5034f5987795aab22ed27c42637 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:11 +0100 +Subject: netfilter: xtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit a9525c7f6219cee9284c0031c5930e8d41384677 ] + +Add hidden IP(6)_NF_IPTABLES_LEGACY symbol. + +When any of the "old" builtin tables are enabled the "old" iptables +interface will be supported. + +To disable the old set/getsockopt interface the existing options +for the builtin tables need to be turned off: + +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER is not set +CONFIG_IP_NF_NAT is not set +CONFIG_IP_NF_MANGLE is not set +CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_SECURITY is not set + +Same for CONFIG_IP6_NF_ variants. + +This allows to build a kernel that only supports ip(6)tables-nft +(iptables-over-nftables api). + +In the future the _LEGACY symbol will become visible and the select +statements will be turned into 'depends on', but for now be on safe side +so "make oldconfig" won't break things. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 15 ++++++++++++--- + net/ipv4/netfilter/Makefile | 2 +- + net/ipv6/netfilter/Kconfig | 20 ++++++++++++++------ + net/ipv6/netfilter/Makefile | 2 +- + net/netfilter/Kconfig | 12 ++++++------ + 5 files changed, 34 insertions(+), 17 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 483778f379d44..5ee86c7ae4dcb 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -10,6 +10,10 @@ config NF_DEFRAG_IPV4 + tristate + default n + ++# old sockopt interface and eval loop ++config IP_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" + help +@@ -152,7 +156,7 @@ config IP_NF_MATCH_ECN + config IP_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP_NF_MANGLE || IP_NF_RAW ++ depends on IP_NF_MANGLE || IP_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -173,6 +177,7 @@ config IP_NF_MATCH_TTL + config IP_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -182,7 +187,7 @@ config IP_NF_FILTER + + config IP_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP_NF_FILTER ++ depends on IP_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV4 + default m if NETFILTER_ADVANCED=n + help +@@ -212,6 +217,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT ++ select IP6_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -252,6 +258,7 @@ endif # IP_NF_NAT + config IP_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -275,7 +282,7 @@ config IP_NF_TARGET_CLUSTERIP + + config IP_NF_TARGET_ECN + tristate "ECN target support" +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `ECN' target, which can be used in the iptables mangle +@@ -300,6 +307,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -313,6 +321,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile +index 93bad11842517..2e606a13ee5ff 100644 +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -25,7 +25,7 @@ obj-$(CONFIG_NFT_FIB_IPV4) += nft_fib_ipv4.o + obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o + + # generic IP tables +-obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o ++obj-$(CONFIG_IP_NF_IPTABLES_LEGACY) += ip_tables.o + + # the three instances of ip_tables + obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index 0ba62f4868f97..f3c8e2d918e13 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -6,6 +6,10 @@ + menu "IPv6: Netfilter Configuration" + depends on INET && IPV6 && NETFILTER + ++# old sockopt interface and eval loop ++config IP6_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" + help +@@ -147,7 +151,7 @@ config IP6_NF_MATCH_MH + config IP6_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP6_NF_MANGLE || IP6_NF_RAW ++ depends on IP6_NF_MANGLE || IP6_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -186,6 +190,8 @@ config IP6_NF_TARGET_HL + config IP6_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY ++ tristate + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -195,7 +201,7 @@ config IP6_NF_FILTER + + config IP6_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP6_NF_FILTER ++ depends on IP6_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV6 + default m if NETFILTER_ADVANCED=n + help +@@ -221,6 +227,7 @@ config IP6_NF_TARGET_SYNPROXY + config IP6_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -230,6 +237,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -243,6 +251,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -254,6 +263,7 @@ config IP6_NF_NAT + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED + select NF_NAT ++ select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +@@ -262,25 +272,23 @@ config IP6_NF_NAT + + To compile it as a module, choose M here. If unsure, say N. + +-if IP6_NF_NAT +- + config IP6_NF_TARGET_MASQUERADE + tristate "MASQUERADE target support" + select NETFILTER_XT_TARGET_MASQUERADE ++ depends on IP6_NF_NAT + help + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects NETFILTER_XT_TARGET_MASQUERADE. + + config IP6_NF_TARGET_NPT + tristate "NPT (Network Prefix translation) target support" ++ depends on IP6_NF_NAT || NFT_COMPAT + help + This option adds the `SNPT' and `DNPT' target, which perform + stateless IPv6-to-IPv6 Network Prefix Translation per RFC 6296. + + To compile it as a module, choose M here. If unsure, say N. + +-endif # IP6_NF_NAT +- + endif # IP6_NF_IPTABLES + endmenu + +diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile +index b8d6dc9aeeb6f..66ce6fa5b2f52 100644 +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -4,7 +4,7 @@ + # + + # Link order matters here. +-obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o ++obj-$(CONFIG_IP6_NF_IPTABLES_LEGACY) += ip6_tables.o + obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o + obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index 4b8d04640ff32..344c287aa3f41 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -816,7 +816,7 @@ config NETFILTER_XT_TARGET_AUDIT + + config NETFILTER_XT_TARGET_CHECKSUM + tristate "CHECKSUM target support" +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `CHECKSUM' target, which can be used in the iptables mangle +@@ -867,7 +867,7 @@ config NETFILTER_XT_TARGET_CONNSECMARK + config NETFILTER_XT_TARGET_CT + tristate '"CT" target support' + depends on NF_CONNTRACK +- depends on IP_NF_RAW || IP6_NF_RAW ++ depends on IP_NF_RAW || IP6_NF_RAW || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This options adds a `CT' target, which allows to specify initial +@@ -878,7 +878,7 @@ config NETFILTER_XT_TARGET_CT + + config NETFILTER_XT_TARGET_DSCP + tristate '"DSCP" and "TOS" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `DSCP' target, which allows you to manipulate +@@ -894,7 +894,7 @@ config NETFILTER_XT_TARGET_DSCP + + config NETFILTER_XT_TARGET_HL + tristate '"HL" hoplimit target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds the "HL" (for IPv6) and "TTL" (for IPv4) +@@ -1078,7 +1078,7 @@ config NETFILTER_XT_TARGET_TPROXY + depends on NETFILTER_ADVANCED + depends on IPV6 || IPV6=n + depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + select NF_DEFRAG_IPV4 + select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n + select NF_TPROXY_IPV4 +@@ -1145,7 +1145,7 @@ config NETFILTER_XT_TARGET_TCPMSS + + config NETFILTER_XT_TARGET_TCPOPTSTRIP + tristate '"TCPOPTSTRIP" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a "TCPOPTSTRIP" target, which allows you to strip +-- +2.53.0 + diff --git a/queue-6.1/netfilter-xtables-fix-up-kconfig-dependencies.patch b/queue-6.1/netfilter-xtables-fix-up-kconfig-dependencies.patch new file mode 100644 index 0000000000..8cc336617e --- /dev/null +++ b/queue-6.1/netfilter-xtables-fix-up-kconfig-dependencies.patch @@ -0,0 +1,58 @@ +From 3419026d7dccb97b25c126794f3efbf78b9f6d18 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 Feb 2024 14:55:53 +0100 +Subject: netfilter: xtables: fix up kconfig dependencies + +From: Florian Westphal + +[ Upstream commit 749d4ef0868c5d8a98e07073791b2198178c93b4 ] + +Randy Dunlap reports arptables build failure: +arp_tables.c:(.text+0x20): undefined reference to `xt_find_table' + +... because recent change removed a 'select' on the xtables core. +Add a "depends" clause on arptables to resolve this. + +Kernel test robot reports another build breakage: +iptable_nat.c:(.text+0x8): undefined reference to `ipt_unregister_table_exit' + +... because of a typo, the nat table selected ip6tables. + +Reported-by: kernel test robot +Reported-by: Randy Dunlap +Closes: https://lore.kernel.org/netfilter-devel/d0dfbaef-046a-4c42-9daa-53636664bf6d@infradead.org/ +Fixes: a9525c7f6219 ("netfilter: xtables: allow xtables-nft only builds") +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Acked-by: Randy Dunlap +Tested-by: Randy Dunlap # build-tested +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 5ee86c7ae4dcb..0f60a740d117d 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -217,7 +217,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP6_NF_IPTABLES_LEGACY ++ select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -343,6 +343,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +-- +2.53.0 + diff --git a/queue-6.1/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-6.1/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..182b77d86d --- /dev/null +++ b/queue-6.1/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From e8dda57749952ef717e511bb3617b35fd6da5327 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 8834436bc9dbc..e3a9278c06842 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-6.1/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch b/queue-6.1/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch new file mode 100644 index 0000000000..a649bea4a0 --- /dev/null +++ b/queue-6.1/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch @@ -0,0 +1,56 @@ +From 3f829d076e43af5d2728415537d63900cad75895 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:44:58 +0530 +Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for + sm8150 + +From: Maulik Shah + +[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not +support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC +pin and already reflected in the wake irq map. + +Remove the unsupported wakeup interrupts from the map. + +Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") +Reviewed-by: Konrad Dybcio +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c +index 1cc622694553d..2bda8c1c89583 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c ++++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c +@@ -1504,18 +1504,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, + { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, +- { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, +- { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, ++ { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, ++ { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, + { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, + { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, +- { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, ++ { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, + { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, + { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, + { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, +- { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, ++ { 144, 115 }, { 147, 102 }, { 150, 107 }, + { 152, 108 }, { 153, 109 } + }; + +-- +2.53.0 + diff --git a/queue-6.1/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-6.1/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..4d7de02bf1 --- /dev/null +++ b/queue-6.1/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From 934e41eb596db83a9a8b11792f773acb4fd98953 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 38693b735c876..87b7fd09a6f6f 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-6.1/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-6.1/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..d0fd18994f --- /dev/null +++ b/queue-6.1/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From 9519ed6f6f30a272c5c6ec02c9f1860d2031d213 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 6477591747cfd..690825c9117f0 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-6.1/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-6.1/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..f326df8bcd --- /dev/null +++ b/queue-6.1/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From 4483cdce6aa2784b5094338649d972ef5c2627c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index 761d88929ef97..ddf46fceb1c37 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -638,12 +638,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-6.1/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-6.1/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..8ac84a256c --- /dev/null +++ b/queue-6.1/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From 8aa8cdf419ceddd3acc4daadf6e7fcdd16e540db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index 224139006a433..1b590bbbd8f8f 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -272,12 +272,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-6.1/powerpc-time-remove-redundant-preempt_disable-enable.patch b/queue-6.1/powerpc-time-remove-redundant-preempt_disable-enable.patch new file mode 100644 index 0000000000..c2fded2796 --- /dev/null +++ b/queue-6.1/powerpc-time-remove-redundant-preempt_disable-enable.patch @@ -0,0 +1,98 @@ +From 185dd234f0071113f7359a595ba8a17933a01eb2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 13:44:13 +0530 +Subject: powerpc/time: Remove redundant preempt_disable|enable() calls from + arch_irq_work_raise() + +From: Sayali Patil + +[ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] + +A kernel panic is observed when handling machine check exceptions from +real mode. + + BUG: Unable to handle kernel data access on read at 0xc00000006be21300 + Oops: Kernel access of bad area, sig: 11 [#1] + MSR: 8000000000001003 CR: 88222248 XER: 00000005 + CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 + NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 + LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 + Call Trace: + [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 + [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 + +The crash occurs because arch_irq_work_raise() calls preempt_disable() +from machine check exception (MCE) handlers running in real mode. In +this context, accessing the preempt_count can fault, leading to the panic. + +The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix +oops due to perf_event_do_pending call") to avoid races while raising +irq work from exception context. + +Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when +queueing work on the local CPU") added preemption protection in +irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per +cpu atomics instead of regular atomics") added equivalent +protection in irq_work_queue_on() before reaching arch_irq_work_raise(): + + irq_work_queue() / irq_work_queue_on() + -> preempt_disable() + -> __irq_work_queue_local() + -> irq_work_raise() + -> arch_irq_work_raise() + +As a result, callers other than mce_irq_work_raise() already execute +with preemption disabled, making the additional +preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +redundant. + +The arch_irq_work_raise() function executes in NMI context when called +from MCE handler. Hence we will not be preempted or scheduled out since +we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove +the preempt_disable()/preempt_enable() calls from here. + +Remove it to avoid accessing preempt_count from real mode context. + +Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") +Suggested-by: Mahesh Salgaonkar +Acked-by: Shrikanth Hegde +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Sayali Patil +[Maddy: Fixed the commit title] +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/kernel/time.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c +index 285159e65a3ba..6628b65b9ecad 100644 +--- a/arch/powerpc/kernel/time.c ++++ b/arch/powerpc/kernel/time.c +@@ -454,6 +454,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); + + #endif /* 32 vs 64 bit */ + ++/* ++ * Must be called with preemption disabled since it updates ++ * per-CPU irq_work state and programs the local CPU decrementer. ++ */ + void arch_irq_work_raise(void) + { + /* +@@ -467,10 +471,8 @@ void arch_irq_work_raise(void) + * which could get tangled up if we're messing with the same state + * here. + */ +- preempt_disable(); + set_irq_work_pending_flag(); + set_dec(1); +- preempt_enable(); + } + + static void set_dec_or_work(u64 val) +-- +2.53.0 + diff --git a/queue-6.1/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-6.1/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..8b1cbedf34 --- /dev/null +++ b/queue-6.1/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From 9b9c6b8dd15a7619d811a61bcbfa010c8199d0bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 2a3c9ac64a42e..fade349daf39e 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -296,8 +296,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index db8fd1156f..13bfb2e288 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -897,3 +897,74 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +hid-uclogic-fix-regression-of-input-name-assignment.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch +arm-integrator-fix-early-initialization.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-arptables-allow-xtables-nft-only-builds.patch +netfilter-xtables-allow-xtables-nft-only-builds.patch +netfilter-ebtables-allow-xtables-nft-only-builds.patch +netfilter-xtables-fix-up-kconfig-dependencies.patch +netfilter-arptables-select-netfilter_family_arp-when.patch +netfilter-make-legacy-configs-user-selectable.patch +netfilter-exclude-legacy-tables-on-preempt_rt.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-x_tables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +net-lan966x-avoid-unregistering-netdev-on-register-f.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +irqchip-ath79-cpu-remove-unused-function.patch +irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +powerpc-time-remove-redundant-preempt_disable-enable.patch +net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +wifi-ath11k-trigger-sta-disconnect-on-hardware-resta.patch +wifi-ath11k-update-hw-params-for-ipq5018.patch +wifi-ath11k-update-ce-configurations-for-ipq5018.patch +wifi-ath11k-remap-ce-register-space-for-ipq5018.patch +wifi-ath11k-update-hal-srng-regs-for-ipq5018.patch +wifi-ath11k-initialize-hw_ops-for-ipq5018.patch +wifi-ath11k-add-new-hw-ops-for-ipq5018-to-get-rx-des.patch +wifi-ath11k-fix-rssi-station-dump-not-updated-in-qcn.patch +wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch +net-dsa-mt7530-sync-driver-specific-behavior-of-mt75.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +net-bridge-flush-multicast-groups-when-snooping-is-d.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +net-ag71xx-check-error-for-platform_get_irq.patch +string-add-mem_is_zero-helper-to-check-if-memory-are.patch +gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch diff --git a/queue-6.1/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch b/queue-6.1/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch new file mode 100644 index 0000000000..3170d73133 --- /dev/null +++ b/queue-6.1/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch @@ -0,0 +1,38 @@ +From cff4caa91653d6e4794c0677a258c17f24cd41c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 01:55:37 +0800 +Subject: spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() + +From: Felix Gu + +[ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] + +When DMA read times out in mtk_snand_read_page_cache(), the original code +erroneously jumped to cleanup label which skips DMA unmapping and ECC +disable, causing a resource leak. + +Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") +Signed-off-by: Felix Gu +Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-mtk-snfi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c +index 7afb2202b2d95..ef14ef31eac1d 100644 +--- a/drivers/spi/spi-mtk-snfi.c ++++ b/drivers/spi/spi-mtk-snfi.c +@@ -930,7 +930,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; +- goto cleanup; ++ goto cleanup2; + } + + // Wait for BUS_SEC_CNTR returning expected value +-- +2.53.0 + diff --git a/queue-6.1/string-add-mem_is_zero-helper-to-check-if-memory-are.patch b/queue-6.1/string-add-mem_is_zero-helper-to-check-if-memory-are.patch new file mode 100644 index 0000000000..f52836d4ea --- /dev/null +++ b/queue-6.1/string-add-mem_is_zero-helper-to-check-if-memory-are.patch @@ -0,0 +1,51 @@ +From 35743bca68d8d12836b4449ad3a2020834a125c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Aug 2024 13:00:34 +0300 +Subject: string: add mem_is_zero() helper to check if memory area is all zeros + +From: Jani Nikula + +[ Upstream commit 3942bb49728ad9e1f94d953a88af169a8f5d8099 ] + +Almost two thirds of the memchr_inv() usages check if the memory area is +all zeros, with no interest in where in the buffer the first non-zero +byte is located. Checking for !memchr_inv(s, 0, n) is also not very +intuitive or discoverable. Add an explicit mem_is_zero() helper for this +use case. + +Reviewed-by: Kees Cook +Reviewed-by: Andy Shevchenko +Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com +Signed-off-by: Jani Nikula +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + include/linux/string.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/include/linux/string.h b/include/linux/string.h +index e7ade5223d422..356b941d1ddac 100644 +--- a/include/linux/string.h ++++ b/include/linux/string.h +@@ -212,6 +212,18 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt) + void *memchr_inv(const void *s, int c, size_t n); + char *strreplace(char *s, char old, char new); + ++/** ++ * mem_is_zero - Check if an area of memory is all 0's. ++ * @s: The memory area ++ * @n: The size of the area ++ * ++ * Return: True if the area of memory is all 0's. ++ */ ++static inline bool mem_is_zero(const void *s, size_t n) ++{ ++ return !memchr_inv(s, 0, n); ++} ++ + extern void kfree_const(const void *x); + + extern char *kstrdup(const char *s, gfp_t gfp) __malloc; +-- +2.53.0 + diff --git a/queue-6.1/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-6.1/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..92198e2dbe --- /dev/null +++ b/queue-6.1/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From 62fdbb7d219b9348e24dee5750e6d787f193f158 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index dc32a3d8ef874..a275ab5321a96 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1112,7 +1112,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-6.1/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-6.1/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..89e4f3955e --- /dev/null +++ b/queue-6.1/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 41a7a9138706473769840bda1e803d89f3123b25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index b5276f2f2cf40..336fc54d8ec86 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1351,10 +1351,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-add-new-hw-ops-for-ipq5018-to-get-rx-des.patch b/queue-6.1/wifi-ath11k-add-new-hw-ops-for-ipq5018-to-get-rx-des.patch new file mode 100644 index 0000000000..cd3aa323a2 --- /dev/null +++ b/queue-6.1/wifi-ath11k-add-new-hw-ops-for-ipq5018-to-get-rx-des.patch @@ -0,0 +1,94 @@ +From 80204bcc44fd7bee969cc8bae0eace13e9a1eeb0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:15 +0200 +Subject: wifi: ath11k: add new hw ops for IPQ5018 to get rx dest ring hashmap + +From: Sriram R + +[ Upstream commit 69968f88f1770d61cae0febef805fd00d66cf6a1 ] + +The Destination ring control register is different +for IPQ5018 when compared to IPQ8074/IPQ6018/QCN9074. +Hence create a new hw ops to fetch the hash ring map +for different device variants. ipq5018 hw ops +is similar to qcn9074 except for this change, so reuse +all the qcn9074 ops for ipq5018. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-8-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/hw.c | 44 ++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 7632220469dab..60ac215e06786 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -792,6 +792,49 @@ static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab) + ring_hash_map); + } + ++static void ath11k_hw_ipq5018_reo_setup(struct ath11k_base *ab) ++{ ++ u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; ++ u32 val; ++ ++ /* Each hash entry uses three bits to map to a particular ring. */ ++ u32 ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | ++ HAL_HASH_ROUTING_RING_SW2 << 4 | ++ HAL_HASH_ROUTING_RING_SW3 << 8 | ++ HAL_HASH_ROUTING_RING_SW4 << 12 | ++ HAL_HASH_ROUTING_RING_SW1 << 16 | ++ HAL_HASH_ROUTING_RING_SW2 << 20 | ++ HAL_HASH_ROUTING_RING_SW3 << 24 | ++ HAL_HASH_ROUTING_RING_SW4 << 28; ++ ++ val = ath11k_hif_read32(ab, reo_base + HAL_REO1_GEN_ENABLE); ++ ++ val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; ++ val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, ++ HAL_SRNG_RING_ID_REO2SW1) | ++ FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | ++ FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); ++ ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab), ++ HAL_DEFAULT_REO_TIMEOUT_USEC); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1(ab), ++ HAL_DEFAULT_REO_TIMEOUT_USEC); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2(ab), ++ HAL_DEFAULT_REO_TIMEOUT_USEC); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab), ++ HAL_DEFAULT_REO_TIMEOUT_USEC); ++ ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, ++ ring_hash_map); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_1, ++ ring_hash_map); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, ++ ring_hash_map); ++ ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, ++ ring_hash_map); ++} ++ + static u16 ath11k_hw_ipq8074_mpdu_info_get_peerid(u8 *tlv_data) + { + u16 peer_id = 0; +@@ -1118,6 +1161,7 @@ const struct ath11k_hw_ops ipq5018_ops = { + .rx_desc_get_mpdu_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath11k_hw_qcn9074_rx_desc_set_msdu_len, + .rx_desc_get_attention = ath11k_hw_qcn9074_rx_desc_get_attention, ++ .reo_setup = ath11k_hw_ipq5018_reo_setup, + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-6.1/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..c2050fc6d4 --- /dev/null +++ b/queue-6.1/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,77 @@ +From 6cfd59deec4a78cd2af72e5020784bebffd9a98e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 8b50dbc47300b..31128630d8b62 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -8482,6 +8482,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -8495,14 +8496,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -8517,7 +8524,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch b/queue-6.1/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch new file mode 100644 index 0000000000..ce8e7f7ceb --- /dev/null +++ b/queue-6.1/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch @@ -0,0 +1,73 @@ +From 43315ba8e0289cd7bd3a15e53946cb80eb5f2262 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:50:35 +0100 +Subject: wifi: ath11k: fix peer resolution on rx path when peer_id=0 + +From: Matthew Leach + +[ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] + +It has been observed that on certain chipsets a peer can be assigned +peer_id=0. For reception of non-aggregated MPDUs this is fine as +ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer +based upon the source MAC address. On an aggregated link, the mpdu_start +header is only populated by hardware on the first sub-MSDU. This causes +the peer resolution to be skipped for the subsequent MSDUs and the +encryption type of these frames to be set to an incorrect value, +resulting in these MSDUs being dropped by ieee80211. + +ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 +ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 + +Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and +ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID +of 0 to be resolved. + +Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 + +Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") +Reviewed-by: Baochen Qiang +Signed-off-by: Matthew Leach +Reviewed-by: P Praneesh +Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- + drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index be00ea6fbf8b6..397ce654bb3fd 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2226,8 +2226,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) + + lockdep_assert_held(&ab->base_lock); + +- if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + + if (peer) + return peer; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 47bd937591470..a3fbff807948c 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1468,11 +1468,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; +- u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); +- if (peer_id) +- ppdu_info->peer_id = peer_id; ++ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-fix-rssi-station-dump-not-updated-in-qcn.patch b/queue-6.1/wifi-ath11k-fix-rssi-station-dump-not-updated-in-qcn.patch new file mode 100644 index 0000000000..3797996799 --- /dev/null +++ b/queue-6.1/wifi-ath11k-fix-rssi-station-dump-not-updated-in-qcn.patch @@ -0,0 +1,175 @@ +From 924a5b4064a1baff8c1ecca78eb9b443e0b06615 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Mar 2023 16:57:00 +0200 +Subject: wifi: ath11k: fix rssi station dump not updated in QCN9074 + +From: P Praneesh + +[ Upstream commit 031ffa6c2cd305a57ccc6d610f2decd956b2e7f6 ] + +In QCN9074, station dump signal values display default value which +is -95 dbm, since there is firmware header change for HAL_RX_MPDU_START +between QCN9074 and IPQ8074 which cause wrong peer_id fetch from msdu. +Fix this by updating hal_rx_mpdu_info with corresponding QCN9074 tlv +format. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01695-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: P Praneesh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20230320110312.20639-1-quic_ppranees@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/hal_rx.c | 10 ++++++++- + drivers/net/wireless/ath/ath11k/hal_rx.h | 18 +++++++++++++++- + drivers/net/wireless/ath/ath11k/hw.c | 27 ++++++++++++++++-------- + drivers/net/wireless/ath/ath11k/hw.h | 2 +- + 4 files changed, 45 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index d1785e71ffc98..47bd937591470 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -866,6 +866,12 @@ ath11k_hal_rx_populate_mu_user_info(void *rx_tlv, struct hal_rx_mon_ppdu_info *p + ath11k_hal_rx_populate_byte_count(rx_tlv, ppdu_info, rx_user_status); + } + ++static u16 ath11k_hal_rx_mpduinfo_get_peerid(struct ath11k_base *ab, ++ struct hal_rx_mpdu_info *mpdu_info) ++{ ++ return ab->hw_params.hw_ops->mpdu_info_get_peerid(mpdu_info); ++} ++ + static enum hal_rx_mon_status + ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, +@@ -1460,9 +1466,11 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + break; + } + case HAL_RX_MPDU_START: { ++ struct hal_rx_mpdu_info *mpdu_info = ++ (struct hal_rx_mpdu_info *)tlv_data; + u16 peer_id; + +- peer_id = ab->hw_params.hw_ops->mpdu_info_get_peerid(tlv_data); ++ peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + if (peer_id) + ppdu_info->peer_id = peer_id; + break; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h +index f6bae07abfd3e..47e8208b22e13 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -405,7 +405,7 @@ struct hal_rx_phyrx_rssi_legacy_info { + #define HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855 GENMASK(15, 0) + #define HAL_RX_MPDU_INFO_INFO1_MPDU_LEN GENMASK(13, 0) + +-struct hal_rx_mpdu_info { ++struct hal_rx_mpdu_info_ipq8074 { + __le32 rsvd0; + __le32 info0; + __le32 rsvd1[11]; +@@ -413,12 +413,28 @@ struct hal_rx_mpdu_info { + __le32 rsvd2[9]; + } __packed; + ++struct hal_rx_mpdu_info_qcn9074 { ++ __le32 rsvd0[10]; ++ __le32 info0; ++ __le32 rsvd1[2]; ++ __le32 info1; ++ __le32 rsvd2[9]; ++} __packed; ++ + struct hal_rx_mpdu_info_wcn6855 { + __le32 rsvd0[8]; + __le32 info0; + __le32 rsvd1[14]; + } __packed; + ++struct hal_rx_mpdu_info { ++ union { ++ struct hal_rx_mpdu_info_ipq8074 ipq8074; ++ struct hal_rx_mpdu_info_qcn9074 qcn9074; ++ struct hal_rx_mpdu_info_wcn6855 wcn6855; ++ } u; ++} __packed; ++ + #define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) + struct hal_rx_ppdu_end_duration { + __le32 rsvd0[9]; +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 60ac215e06786..6b4355a68e266 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -835,26 +835,35 @@ static void ath11k_hw_ipq5018_reo_setup(struct ath11k_base *ab) + ring_hash_map); + } + +-static u16 ath11k_hw_ipq8074_mpdu_info_get_peerid(u8 *tlv_data) ++static u16 ++ath11k_hw_ipq8074_mpdu_info_get_peerid(struct hal_rx_mpdu_info *mpdu_info) + { + u16 peer_id = 0; +- struct hal_rx_mpdu_info *mpdu_info = +- (struct hal_rx_mpdu_info *)tlv_data; + + peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID, +- __le32_to_cpu(mpdu_info->info0)); ++ __le32_to_cpu(mpdu_info->u.ipq8074.info0)); + + return peer_id; + } + +-static u16 ath11k_hw_wcn6855_mpdu_info_get_peerid(u8 *tlv_data) ++static u16 ++ath11k_hw_qcn9074_mpdu_info_get_peerid(struct hal_rx_mpdu_info *mpdu_info) ++{ ++ u16 peer_id = 0; ++ ++ peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID, ++ __le32_to_cpu(mpdu_info->u.qcn9074.info0)); ++ ++ return peer_id; ++} ++ ++static u16 ++ath11k_hw_wcn6855_mpdu_info_get_peerid(struct hal_rx_mpdu_info *mpdu_info) + { + u16 peer_id = 0; +- struct hal_rx_mpdu_info_wcn6855 *mpdu_info = +- (struct hal_rx_mpdu_info_wcn6855 *)tlv_data; + + peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855, +- __le32_to_cpu(mpdu_info->info0)); ++ __le32_to_cpu(mpdu_info->u.wcn6855.info0)); + return peer_id; + } + +@@ -1042,7 +1051,7 @@ const struct ath11k_hw_ops qcn9074_ops = { + .rx_desc_get_attention = ath11k_hw_qcn9074_rx_desc_get_attention, + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, +- .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .mpdu_info_get_peerid = ath11k_hw_qcn9074_mpdu_info_get_peerid, + .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 9f45d061d8265..6a5dd2dbdb3ab 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -263,7 +263,7 @@ struct ath11k_hw_ops { + struct rx_attention *(*rx_desc_get_attention)(struct hal_rx_desc *desc); + u8 *(*rx_desc_get_msdu_payload)(struct hal_rx_desc *desc); + void (*reo_setup)(struct ath11k_base *ab); +- u16 (*mpdu_info_get_peerid)(u8 *tlv_data); ++ u16 (*mpdu_info_get_peerid)(struct hal_rx_mpdu_info *mpdu_info); + bool (*rx_desc_mac_addr2_valid)(struct hal_rx_desc *desc); + u8* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc); + u32 (*get_ring_selector)(struct sk_buff *skb); +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-initialize-hw_ops-for-ipq5018.patch b/queue-6.1/wifi-ath11k-initialize-hw_ops-for-ipq5018.patch new file mode 100644 index 0000000000..bee141e385 --- /dev/null +++ b/queue-6.1/wifi-ath11k-initialize-hw_ops-for-ipq5018.patch @@ -0,0 +1,105 @@ +From 7643f5ff026094ac8bb18f00845d90d33a919e10 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:14 +0200 +Subject: wifi: ath11k: initialize hw_ops for IPQ5018 + +From: Sriram R + +[ Upstream commit ba60f2793d3a37a00da14bb56a26558a902d2831 ] + +The ipq5018_ops is initialized for IPQ5018. This is different from +other platforms. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-7-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/hw.c | 40 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 3 files changed, 42 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index a5a9b485a50d2..be7d0644a6e8f 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -635,6 +635,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + }, + .internal_sleep_clock = false, + .regs = &ipq5018_regs, ++ .hw_ops = &ipq5018_ops, + .host_ce_config = ath11k_host_ce_config_qcn9074, + .ce_count = CE_CNT_5018, + .target_ce_config = ath11k_target_ce_config_wlan_ipq5018, +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 6135a45f255d1..7632220469dab 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -1085,6 +1085,46 @@ const struct ath11k_hw_ops wcn6750_ops = { + .get_ring_selector = ath11k_hw_wcn6750_get_tcl_ring_selector, + }; + ++/* IPQ5018 hw ops is similar to QCN9074 except for the dest ring remap */ ++const struct ath11k_hw_ops ipq5018_ops = { ++ .get_hw_mac_from_pdev_id = ath11k_hw_ipq6018_mac_from_pdev_id, ++ .wmi_init_config = ath11k_init_wmi_config_ipq8074, ++ .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_ipq8074, ++ .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_ipq8074, ++ .tx_mesh_enable = ath11k_hw_qcn9074_tx_mesh_enable, ++ .rx_desc_get_first_msdu = ath11k_hw_qcn9074_rx_desc_get_first_msdu, ++ .rx_desc_get_last_msdu = ath11k_hw_qcn9074_rx_desc_get_last_msdu, ++ .rx_desc_get_l3_pad_bytes = ath11k_hw_qcn9074_rx_desc_get_l3_pad_bytes, ++ .rx_desc_get_hdr_status = ath11k_hw_qcn9074_rx_desc_get_hdr_status, ++ .rx_desc_encrypt_valid = ath11k_hw_qcn9074_rx_desc_encrypt_valid, ++ .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, ++ .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, ++ .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, ++ .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, ++ .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, ++ .rx_desc_get_mpdu_start_seq_no = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_seq_no, ++ .rx_desc_get_msdu_len = ath11k_hw_qcn9074_rx_desc_get_msdu_len, ++ .rx_desc_get_msdu_sgi = ath11k_hw_qcn9074_rx_desc_get_msdu_sgi, ++ .rx_desc_get_msdu_rate_mcs = ath11k_hw_qcn9074_rx_desc_get_msdu_rate_mcs, ++ .rx_desc_get_msdu_rx_bw = ath11k_hw_qcn9074_rx_desc_get_msdu_rx_bw, ++ .rx_desc_get_msdu_freq = ath11k_hw_qcn9074_rx_desc_get_msdu_freq, ++ .rx_desc_get_msdu_pkt_type = ath11k_hw_qcn9074_rx_desc_get_msdu_pkt_type, ++ .rx_desc_get_msdu_nss = ath11k_hw_qcn9074_rx_desc_get_msdu_nss, ++ .rx_desc_get_mpdu_tid = ath11k_hw_qcn9074_rx_desc_get_mpdu_tid, ++ .rx_desc_get_mpdu_peer_id = ath11k_hw_qcn9074_rx_desc_get_mpdu_peer_id, ++ .rx_desc_copy_attn_end_tlv = ath11k_hw_qcn9074_rx_desc_copy_attn_end, ++ .rx_desc_get_mpdu_start_tag = ath11k_hw_qcn9074_rx_desc_get_mpdu_start_tag, ++ .rx_desc_get_mpdu_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_mpdu_ppdu_id, ++ .rx_desc_set_msdu_len = ath11k_hw_qcn9074_rx_desc_set_msdu_len, ++ .rx_desc_get_attention = ath11k_hw_qcn9074_rx_desc_get_attention, ++ .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, ++ .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, ++ .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, ++ ++}; ++ + #define ATH11K_TX_RING_MASK_0 BIT(0) + #define ATH11K_TX_RING_MASK_1 BIT(1) + #define ATH11K_TX_RING_MASK_2 BIT(2) +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index b8afd51d0c1ea..9f45d061d8265 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -275,6 +275,7 @@ extern const struct ath11k_hw_ops qca6390_ops; + extern const struct ath11k_hw_ops qcn9074_ops; + extern const struct ath11k_hw_ops wcn6855_ops; + extern const struct ath11k_hw_ops wcn6750_ops; ++extern const struct ath11k_hw_ops ipq5018_ops; + + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-remap-ce-register-space-for-ipq5018.patch b/queue-6.1/wifi-ath11k-remap-ce-register-space-for-ipq5018.patch new file mode 100644 index 0000000000..640a172ffd --- /dev/null +++ b/queue-6.1/wifi-ath11k-remap-ce-register-space-for-ipq5018.patch @@ -0,0 +1,378 @@ +From db48fa0e8ef2406a01446bb10789ba49a644564a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:14 +0200 +Subject: wifi: ath11k: remap ce register space for IPQ5018 + +From: Sriram R + +[ Upstream commit b42b3678c91f3ca6e0888bf5a15c1e8678fd5f2d ] + +In IPQ5018 ce register space is moved out of wcss unlike +ipq8074 or ipq6018 and the space is not contiguous, +hence remap the CE registers to a new space to access them. + +Register read/write is modified to check if the register to be written +falls in the CE register space and corresponding register is written. +Also adjust the interrupt register address to ce irq enable/disable. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-5-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/ahb.c | 44 ++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/ce.h | 16 ++++++++++ + drivers/net/wireless/ath/ath11k/core.c | 8 +++++ + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/hal.c | 17 ++++++---- + drivers/net/wireless/ath/ath11k/hal.h | 5 +++ + drivers/net/wireless/ath/ath11k/hw.c | 17 ++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 9 ++++++ + drivers/net/wireless/ath/ath11k/pci.c | 2 ++ + 9 files changed, 107 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c +index 70d468f013383..db20b39c0bf56 100644 +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -267,30 +267,42 @@ static void ath11k_ahb_clearbit32(struct ath11k_base *ab, u8 bit, u32 offset) + static void ath11k_ahb_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) + { + const struct ce_attr *ce_attr; ++ const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; ++ u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; ++ ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +- ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_ADDRESS); ++ ath11k_ahb_setbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { +- ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); ++ ath11k_ahb_setbit32(ab, ce_id, ie2_reg_addr); + ath11k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, +- CE_HOST_IE_3_ADDRESS); ++ ie3_reg_addr); + } + } + + static void ath11k_ahb_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) + { + const struct ce_attr *ce_attr; ++ const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; ++ u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; ++ ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +- ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_ADDRESS); ++ ath11k_ahb_clearbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { +- ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); ++ ath11k_ahb_clearbit32(ab, ce_id, ie2_reg_addr); + ath11k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, +- CE_HOST_IE_3_ADDRESS); ++ ie3_reg_addr); + } + } + +@@ -1148,10 +1160,26 @@ static int ath11k_ahb_probe(struct platform_device *pdev) + goto err_core_free; + } + ++ ab->mem_ce = ab->mem; ++ + ret = ath11k_core_pre_init(ab); + if (ret) + goto err_core_free; + ++ if (ab->hw_params.ce_remap) { ++ const struct ce_remap *ce_remap = ab->hw_params.ce_remap; ++ /* ce register space is moved out of wcss unlike ipq8074 or ipq6018 ++ * and the space is not contiguous, hence remapping the CE registers ++ * to a new space for accessing them. ++ */ ++ ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); ++ if (IS_ERR(ab->mem_ce)) { ++ dev_err(&pdev->dev, "ce ioremap error\n"); ++ ret = -ENOMEM; ++ goto err_core_free; ++ } ++ } ++ + ret = ath11k_ahb_setup_resources(ab); + if (ret) + goto err_core_free; +@@ -1242,6 +1270,10 @@ static void ath11k_ahb_free_resources(struct ath11k_base *ab) + ath11k_ahb_release_smp2p_handle(ab); + ath11k_ahb_fw_resource_deinit(ab); + ath11k_ce_free_pipes(ab); ++ ++ if (ab->hw_params.ce_remap) ++ iounmap(ab->mem_ce); ++ + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + } +diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h +index 9644ff909502e..1fc6360e7f01b 100644 +--- a/drivers/net/wireless/ath/ath11k/ce.h ++++ b/drivers/net/wireless/ath/ath11k/ce.h +@@ -49,6 +49,11 @@ void ath11k_ce_byte_swap(void *mem, u32 len); + #define CE_HOST_IE_2_ADDRESS 0x00A18040 + #define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS + ++/* CE IE registers are different for IPQ5018 */ ++#define CE_HOST_IPQ5018_IE_ADDRESS 0x0841804C ++#define CE_HOST_IPQ5018_IE_2_ADDRESS 0x08418050 ++#define CE_HOST_IPQ5018_IE_3_ADDRESS CE_HOST_IPQ5018_IE_ADDRESS ++ + #define CE_HOST_IE_3_SHIFT 0xC + + #define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) +@@ -84,6 +89,17 @@ struct ce_pipe_config { + __le32 reserved; + }; + ++struct ce_ie_addr { ++ u32 ie1_reg_addr; ++ u32 ie2_reg_addr; ++ u32 ie3_reg_addr; ++}; ++ ++struct ce_remap { ++ u32 base; ++ u32 size; ++}; ++ + struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 4c234d576b3d9..ce87e67dc638c 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -54,6 +54,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 11, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq8074, + .svc_to_ce_map_len = 21, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, +@@ -137,6 +138,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 11, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq6018, + .svc_to_ce_map_len = 19, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = false, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, +@@ -218,6 +220,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, +@@ -301,6 +304,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qcn9074, + .svc_to_ce_map_len = 18, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, +@@ -381,6 +385,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, +@@ -546,6 +551,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq8074, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 1, +@@ -634,6 +640,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .target_ce_count = TARGET_CE_CNT_5018, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018, + .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018, ++ .ce_ie_addr = &ath11k_ce_ie_addr_ipq5018, ++ .ce_remap = &ath11k_ce_remap_ipq5018, + .rxdma1_enable = true, + .num_rxmda_per_pdev = RXDMA_PER_PDEV_5018, + .rx_mac_buf_ring = false, +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index c0ddcf7bcd90b..2e4f89bac61b1 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -853,6 +853,7 @@ struct ath11k_base { + struct ath11k_dp dp; + + void __iomem *mem; ++ void __iomem *mem_ce; + unsigned long mem_len; + + struct { +diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c +index e4114cc35b10c..87ed147c5968d 100644 +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -1245,16 +1245,20 @@ static int ath11k_hal_srng_create_config(struct ath11k_base *ab) + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + s = &hal->srng_config[HAL_CE_SRC]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB + ++ ATH11K_CE_OFFSET(ab); ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP + ++ ATH11K_CE_OFFSET(ab); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + + s = &hal->srng_config[HAL_CE_DST]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB + ++ ATH11K_CE_OFFSET(ab); ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP + ++ ATH11K_CE_OFFSET(ab); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +@@ -1262,8 +1266,9 @@ static int ath11k_hal_srng_create_config(struct ath11k_base *ab) + + s = &hal->srng_config[HAL_CE_DST_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + +- HAL_CE_DST_STATUS_RING_BASE_LSB; +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; ++ HAL_CE_DST_STATUS_RING_BASE_LSB + ATH11K_CE_OFFSET(ab); ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP + ++ ATH11K_CE_OFFSET(ab); + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h +index 84b070b479582..f2341acf0730f 100644 +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -321,6 +321,10 @@ struct ath11k_base; + #define HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE 0x000fffff + #define HAL_RXDMA_RING_MAX_SIZE 0x0000ffff + ++/* IPQ5018 ce registers */ ++#define HAL_IPQ5018_CE_WFSS_REG_BASE 0x08400000 ++#define HAL_IPQ5018_CE_SIZE 0x200000 ++ + /* Add any other errors here and return them in + * ath11k_hal_rx_desc_get_err(). + */ +@@ -519,6 +523,7 @@ enum hal_srng_dir { + #define HAL_SRNG_FLAGS_MSI_INTR 0x00020000 + #define HAL_SRNG_FLAGS_CACHED 0x20000000 + #define HAL_SRNG_FLAGS_LMAC_RING 0x80000000 ++#define HAL_SRNG_FLAGS_REMAP_CE_RING 0x10000000 + + #define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1) + #define HAL_SRNG_TLV_HDR_LEN GENMASK(25, 10) +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 1928da8415518..5639e261d834e 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2164,6 +2164,23 @@ const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq5018[] = { + { /* terminator entry */ } + }; + ++const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074 = { ++ .ie1_reg_addr = CE_HOST_IE_ADDRESS, ++ .ie2_reg_addr = CE_HOST_IE_2_ADDRESS, ++ .ie3_reg_addr = CE_HOST_IE_3_ADDRESS, ++}; ++ ++const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018 = { ++ .ie1_reg_addr = CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie2_reg_addr = CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie3_reg_addr = CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++}; ++ ++const struct ce_remap ath11k_ce_remap_ipq5018 = { ++ .base = HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .size = HAL_IPQ5018_CE_SIZE, ++}; ++ + const struct ath11k_hw_regs ipq8074_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000510, +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 0c5ef8a526d85..e2ed5d0477430 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -80,6 +80,8 @@ + #define ATH11K_M3_FILE "m3.bin" + #define ATH11K_REGDB_FILE_NAME "regdb.bin" + ++#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem) ++ + enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, + ATH11K_HW_RATE_CCK_LP_5_5M, +@@ -158,6 +160,8 @@ struct ath11k_hw_params { + u32 target_ce_count; + const struct service_to_pipe *svc_to_ce_map; + u32 svc_to_ce_map_len; ++ const struct ce_ie_addr *ce_ie_addr; ++ const struct ce_remap *ce_remap; + + bool single_pdev_only; + +@@ -277,6 +281,11 @@ extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750; + ++extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074; ++extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018; ++ ++extern const struct ce_remap ath11k_ce_remap_ipq5018; ++ + extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074; + extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390; + extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_wcn6750; +diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c +index 79d2876a46b53..a8431ce1ab9ac 100644 +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -543,6 +543,8 @@ static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev) + goto clear_master; + } + ++ ab->mem_ce = ab->mem; ++ + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem); + return 0; + +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-trigger-sta-disconnect-on-hardware-resta.patch b/queue-6.1/wifi-ath11k-trigger-sta-disconnect-on-hardware-resta.patch new file mode 100644 index 0000000000..3072b42ede --- /dev/null +++ b/queue-6.1/wifi-ath11k-trigger-sta-disconnect-on-hardware-resta.patch @@ -0,0 +1,134 @@ +From 0099f986135714443d926066e5518a70a682d0ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 4 Nov 2022 14:24:03 +0530 +Subject: wifi: ath11k: Trigger sta disconnect on hardware restart + +From: Youghandhar Chintala + +[ Upstream commit a018750a2cceaf4427c4ee3d9ce3e83a171d5bd6 ] + +Currently after the hardware restart triggered from the driver, the +station interface connection remains intact, since a disconnect trigger +is not sent to userspace. This can lead to a problem in targets where +the wifi mac sequence is added by the firmware. + +After the target restart, its wifi mac sequence number gets reset to +zero. Hence AP to which our device is connected will receive frames with +a wifi mac sequence number jump to the past, thereby resulting in the +AP dropping all these frames, until the frame arrives with a wifi mac +sequence number which AP was expecting. + +To avoid such frame drops, its better to trigger a station disconnect +upon target hardware restart which can be done with API +ieee80211_reconfig_disconnect exposed to mac80211. + +The other targets are not affected by this change, since the hardware +params flag is not set. + +Reported-by: kernel test robot + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Youghandhar Chintala +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221104085403.11025-1-quic_youghand@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ + 3 files changed, 14 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 9b5349230ad34..7a61c84939cb3 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -195,6 +195,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = false, + }, + { + .name = "qca6390 hw2.0", +@@ -277,6 +278,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -356,6 +358,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -438,6 +441,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -519,6 +523,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "wcn6750 hw1.0", +@@ -597,6 +602,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .tcl_ring_retry = false, + .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, + .smp2p_wow_exit = true, ++ .support_fw_mac_sequence = true, + }, + }; + +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index 8a3f24862edc4..0c5ef8a526d85 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -219,6 +219,7 @@ struct ath11k_hw_params { + bool tcl_ring_retry; + u32 tx_ring_size; + bool smp2p_wow_exit; ++ bool support_fw_mac_sequence; + }; + + struct ath11k_hw_ops { +diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c +index 6a244f110dca6..2ef03ad1c9051 100644 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7981,6 +7981,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + int recovery_count; ++ struct ath11k_vif *arvif; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; +@@ -8016,6 +8017,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, + ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); + } + } ++ if (ar->ab->hw_params.support_fw_mac_sequence) { ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) ++ ieee80211_hw_restart_disconnect(arvif->vif); ++ } ++ } + } + + mutex_unlock(&ar->conf_mutex); +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-update-ce-configurations-for-ipq5018.patch b/queue-6.1/wifi-ath11k-update-ce-configurations-for-ipq5018.patch new file mode 100644 index 0000000000..63aac911f4 --- /dev/null +++ b/queue-6.1/wifi-ath11k-update-ce-configurations-for-ipq5018.patch @@ -0,0 +1,261 @@ +From 8328fd26c015fa9f168a8978094252bd7b8f6757 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:14 +0200 +Subject: wifi: ath11k: update ce configurations for IPQ5018 + +From: Sriram R + +[ Upstream commit 26af7aabd2d8225c6b2056234626ba5099610871 ] + +IPQ5018 is a single pdev device. Update host +and target CE configurations accordingly. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-4-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/core.c | 4 + + drivers/net/wireless/ath/ath11k/core.h | 3 + + drivers/net/wireless/ath/ath11k/hw.c | 191 +++++++++++++++++++++++++ + 3 files changed, 198 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index c8fb72d9613fa..4c234d576b3d9 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -630,6 +630,10 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .internal_sleep_clock = false, + .host_ce_config = ath11k_host_ce_config_qcn9074, + .ce_count = CE_CNT_5018, ++ .target_ce_config = ath11k_target_ce_config_wlan_ipq5018, ++ .target_ce_count = TARGET_CE_CNT_5018, ++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018, ++ .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018, + .rxdma1_enable = true, + .num_rxmda_per_pdev = RXDMA_PER_PDEV_5018, + .rx_mac_buf_ring = false, +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index 09e40ff461730..c0ddcf7bcd90b 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -1147,6 +1147,9 @@ extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq6018 + extern const struct ce_pipe_config ath11k_target_ce_config_wlan_qca6390[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_qca6390[]; + ++extern const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[]; ++extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq5018[]; ++ + extern const struct ce_pipe_config ath11k_target_ce_config_wlan_qcn9074[]; + extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_qcn9074[]; + int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab); +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 332664643c7b4..1928da8415518 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -1973,6 +1973,197 @@ const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750 = { + }, + }; + ++/* Target firmware's Copy Engine configuration for IPQ5018 */ ++const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = { ++ /* CE0: host->target HTC control and raw streams */ ++ { ++ .pipenum = __cpu_to_le32(0), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE1: target->host HTT + HTC control */ ++ { ++ .pipenum = __cpu_to_le32(1), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE2: target->host WMI */ ++ { ++ .pipenum = __cpu_to_le32(2), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE3: host->target WMI */ ++ { ++ .pipenum = __cpu_to_le32(3), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE4: host->target HTT */ ++ { ++ .pipenum = __cpu_to_le32(4), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), ++ .nentries = __cpu_to_le32(256), ++ .nbytes_max = __cpu_to_le32(256), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE5: target->host Pktlog */ ++ { ++ .pipenum = __cpu_to_le32(5), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE6: Reserved for target autonomous hif_memcpy */ ++ { ++ .pipenum = __cpu_to_le32(6), ++ .pipedir = __cpu_to_le32(PIPEDIR_INOUT), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(16384), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE7 used only by Host */ ++ { ++ .pipenum = __cpu_to_le32(7), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(2048), ++ .flags = __cpu_to_le32(0x2000), ++ .reserved = __cpu_to_le32(0), ++ }, ++ ++ /* CE8 target->host used only by IPA */ ++ { ++ .pipenum = __cpu_to_le32(8), ++ .pipedir = __cpu_to_le32(PIPEDIR_INOUT), ++ .nentries = __cpu_to_le32(32), ++ .nbytes_max = __cpu_to_le32(16384), ++ .flags = __cpu_to_le32(CE_ATTR_FLAGS), ++ .reserved = __cpu_to_le32(0), ++ }, ++}; ++ ++/* Map from service/endpoint to Copy Engine for IPQ5018. ++ * This table is derived from the CE TABLE, above. ++ * It is passed to the Target at startup for use by firmware. ++ */ ++const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq5018[] = { ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(3), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(2), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(3), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(2), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(3), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(2), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(3), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(2), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(3), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(2), ++ }, ++ ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(0), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(1), ++ }, ++ ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(0), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(1), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), ++ .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ ++ .pipenum = __cpu_to_le32(4), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(1), ++ }, ++ { ++ .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_PKT_LOG), ++ .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ ++ .pipenum = __cpu_to_le32(5), ++ }, ++ ++ /* (Additions here) */ ++ ++ { /* terminator entry */ } ++}; ++ + const struct ath11k_hw_regs ipq8074_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000510, +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-update-hal-srng-regs-for-ipq5018.patch b/queue-6.1/wifi-ath11k-update-hal-srng-regs-for-ipq5018.patch new file mode 100644 index 0000000000..7e3576307e --- /dev/null +++ b/queue-6.1/wifi-ath11k-update-hal-srng-regs-for-ipq5018.patch @@ -0,0 +1,145 @@ +From a6e0710d52b9da9984e6e9ab00c17b3666f2c675 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:14 +0200 +Subject: wifi: ath11k: update hal srng regs for IPQ5018 + +From: Sriram R + +[ Upstream commit 711b80acbdfb9667a9cf8374e13320a6e624ce73 ] + +IPQ5018 hal srng register address & offsets are not +similar to IPQ8074/IPQ6018/QCN9074, hence define a +new set of srng register group data for IPQ5018. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-6-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + + drivers/net/wireless/ath/ath11k/hw.c | 79 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 3 files changed, 81 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index ce87e67dc638c..a5a9b485a50d2 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -634,6 +634,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .max_fft_bins = 1024, + }, + .internal_sleep_clock = false, ++ .regs = &ipq5018_regs, + .host_ce_config = ath11k_host_ce_config_qcn9074, + .ce_count = CE_CNT_5018, + .target_ce_config = ath11k_target_ce_config_wlan_ipq5018, +diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c +index 5639e261d834e..6135a45f255d1 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2646,6 +2646,85 @@ static const struct ath11k_hw_tcl2wbm_rbm_map ath11k_hw_tcl2wbm_rbm_map_wcn6750[ + }, + }; + ++const struct ath11k_hw_regs ipq5018_regs = { ++ /* SW2TCL(x) R0 ring configuration address */ ++ .hal_tcl1_ring_base_lsb = 0x00000694, ++ .hal_tcl1_ring_base_msb = 0x00000698, ++ .hal_tcl1_ring_id = 0x0000069c, ++ .hal_tcl1_ring_misc = 0x000006a4, ++ .hal_tcl1_ring_tp_addr_lsb = 0x000006b0, ++ .hal_tcl1_ring_tp_addr_msb = 0x000006b4, ++ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4, ++ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8, ++ .hal_tcl1_ring_msi1_base_lsb = 0x000006dc, ++ .hal_tcl1_ring_msi1_base_msb = 0x000006e0, ++ .hal_tcl1_ring_msi1_data = 0x000006e4, ++ .hal_tcl2_ring_base_lsb = 0x000006ec, ++ .hal_tcl_ring_base_lsb = 0x0000079c, ++ ++ /* TCL STATUS ring address */ ++ .hal_tcl_status_ring_base_lsb = 0x000008a4, ++ ++ /* REO2SW(x) R0 ring configuration address */ ++ .hal_reo1_ring_base_lsb = 0x000001ec, ++ .hal_reo1_ring_base_msb = 0x000001f0, ++ .hal_reo1_ring_id = 0x000001f4, ++ .hal_reo1_ring_misc = 0x000001fc, ++ .hal_reo1_ring_hp_addr_lsb = 0x00000200, ++ .hal_reo1_ring_hp_addr_msb = 0x00000204, ++ .hal_reo1_ring_producer_int_setup = 0x00000210, ++ .hal_reo1_ring_msi1_base_lsb = 0x00000234, ++ .hal_reo1_ring_msi1_base_msb = 0x00000238, ++ .hal_reo1_ring_msi1_data = 0x0000023c, ++ .hal_reo2_ring_base_lsb = 0x00000244, ++ .hal_reo1_aging_thresh_ix_0 = 0x00000564, ++ .hal_reo1_aging_thresh_ix_1 = 0x00000568, ++ .hal_reo1_aging_thresh_ix_2 = 0x0000056c, ++ .hal_reo1_aging_thresh_ix_3 = 0x00000570, ++ ++ /* REO2SW(x) R2 ring pointers (head/tail) address */ ++ .hal_reo1_ring_hp = 0x00003028, ++ .hal_reo1_ring_tp = 0x0000302c, ++ .hal_reo2_ring_hp = 0x00003030, ++ ++ /* REO2TCL R0 ring configuration address */ ++ .hal_reo_tcl_ring_base_lsb = 0x000003fc, ++ .hal_reo_tcl_ring_hp = 0x00003058, ++ ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x0000013c, ++ .hal_sw2reo_ring_hp = 0x00003018, ++ ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x000000e4, ++ .hal_reo_cmd_ring_hp = 0x00003010, ++ ++ /* REO status address */ ++ .hal_reo_status_ring_base_lsb = 0x00000504, ++ .hal_reo_status_hp = 0x00003070, ++ ++ /* WCSS relative address */ ++ .hal_seq_wcss_umac_ce0_src_reg = 0x08400000 ++ - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .hal_seq_wcss_umac_ce0_dst_reg = 0x08401000 ++ - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .hal_seq_wcss_umac_ce1_src_reg = 0x08402000 ++ - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .hal_seq_wcss_umac_ce1_dst_reg = 0x08403000 ++ - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ ++ /* WBM Idle address */ ++ .hal_wbm_idle_link_ring_base_lsb = 0x00000874, ++ .hal_wbm_idle_link_ring_misc = 0x00000884, ++ ++ /* SW2WBM release address */ ++ .hal_wbm_release_ring_base_lsb = 0x000001ec, ++ ++ /* WBM2SW release address */ ++ .hal_wbm0_release_ring_base_lsb = 0x00000924, ++ .hal_wbm1_release_ring_base_lsb = 0x0000097c, ++}; ++ + const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074, +diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h +index e2ed5d0477430..b8afd51d0c1ea 100644 +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -415,6 +415,7 @@ extern const struct ath11k_hw_regs qca6390_regs; + extern const struct ath11k_hw_regs qcn9074_regs; + extern const struct ath11k_hw_regs wcn6855_regs; + extern const struct ath11k_hw_regs wcn6750_regs; ++extern const struct ath11k_hw_regs ipq5018_regs; + + static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) + { +-- +2.53.0 + diff --git a/queue-6.1/wifi-ath11k-update-hw-params-for-ipq5018.patch b/queue-6.1/wifi-ath11k-update-hw-params-for-ipq5018.patch new file mode 100644 index 0000000000..aa860355a8 --- /dev/null +++ b/queue-6.1/wifi-ath11k-update-hw-params-for-ipq5018.patch @@ -0,0 +1,138 @@ +From 2e0968fc81bbb45a0b671ea2104768a695a70745 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Dec 2022 23:37:13 +0200 +Subject: wifi: ath11k: update hw params for IPQ5018 + +From: Sriram R + +[ Upstream commit 8dfe875aa24aec68baf6702018633c84c2c1feca ] + +Add new compatible string for IPQ5018 and add +required hw params for IPQ5018. The hw descriptors size and +datapath ops are similar to QCN9074, hence reuse the same. + +Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sriram R +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221122132152.17771-3-quic_kathirve@quicinc.com +Stable-dep-of: 2a2451a34afd ("wifi: ath11k: fix peer resolution on rx path when peer_id=0") +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/core.c | 71 ++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 8 +++ + 2 files changed, 79 insertions(+) + +diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c +index 7a61c84939cb3..c8fb72d9613fa 100644 +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -604,6 +604,77 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { + .smp2p_wow_exit = true, + .support_fw_mac_sequence = true, + }, ++ { ++ .hw_rev = ATH11K_HW_IPQ5018_HW10, ++ .name = "ipq5018 hw1.0", ++ .fw = { ++ .dir = "IPQ5018/hw1.0", ++ .board_size = 256 * 1024, ++ .cal_offset = 128 * 1024, ++ }, ++ .max_radios = MAX_RADIOS_5018, ++ .bdf_addr = 0x4BA00000, ++ /* hal_desc_sz and hw ops are similar to qcn9074 */ ++ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074, ++ .ring_mask = &ath11k_hw_ring_mask_ipq8074, ++ .credit_flow = false, ++ .max_tx_ring = 1, ++ .spectral = { ++ .fft_sz = 2, ++ .fft_pad_sz = 0, ++ .summary_pad_sz = 16, ++ .fft_hdr_len = 24, ++ .max_fft_bins = 1024, ++ }, ++ .internal_sleep_clock = false, ++ .host_ce_config = ath11k_host_ce_config_qcn9074, ++ .ce_count = CE_CNT_5018, ++ .rxdma1_enable = true, ++ .num_rxmda_per_pdev = RXDMA_PER_PDEV_5018, ++ .rx_mac_buf_ring = false, ++ .vdev_start_delay = false, ++ .htt_peer_map_v2 = true, ++ .interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_AP) | ++ BIT(NL80211_IFTYPE_MESH_POINT), ++ .supports_monitor = false, ++ .supports_sta_ps = false, ++ .supports_shadow_regs = false, ++ .fw_mem_mode = 0, ++ .num_vdevs = 16 + 1, ++ .num_peers = 512, ++ .supports_regdb = false, ++ .idle_ps = false, ++ .supports_suspend = false, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .single_pdev_only = false, ++ .cold_boot_calib = true, ++ .fix_l1ss = true, ++ .supports_dynamic_smps_6ghz = false, ++ .alloc_cacheable_memory = true, ++ .supports_rssi_stats = false, ++ .fw_wmi_diag_event = false, ++ .current_cc_support = false, ++ .dbr_debug_support = true, ++ .global_reset = false, ++ .bios_sar_capa = NULL, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = true, ++ .fixed_mem_region = true, ++ .static_window_map = false, ++ .hybrid_bus_type = false, ++ .fixed_fw_mem = false, ++ .support_off_channel_tx = false, ++ .supports_multi_bssid = false, ++ ++ .sram_dump = {}, ++ ++ .tcl_ring_retry = true, ++ .tx_ring_size = DP_TCL_DATA_RING_SIZE, ++ .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = false, ++ }, + }; + + static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) +diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h +index a46fe85e592d8..09e40ff461730 100644 +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -142,6 +142,7 @@ enum ath11k_hw_rev { + ATH11K_HW_WCN6855_HW20, + ATH11K_HW_WCN6855_HW21, + ATH11K_HW_WCN6750_HW10, ++ ATH11K_HW_IPQ5018_HW10, + }; + + enum ath11k_firmware_mode { +@@ -230,6 +231,13 @@ struct ath11k_he { + + #define MAX_RADIOS 3 + ++/* ipq5018 hw param macros */ ++#define MAX_RADIOS_5018 1 ++#define CE_CNT_5018 6 ++#define TARGET_CE_CNT_5018 9 ++#define SVC_CE_MAP_LEN_5018 17 ++#define RXDMA_PER_PDEV_5018 1 ++ + enum { + WMI_HOST_TP_SCALE_MAX = 0, + WMI_HOST_TP_SCALE_50 = 1, +-- +2.53.0 + diff --git a/queue-6.12/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch b/queue-6.12/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch new file mode 100644 index 0000000000..d3bdaed405 --- /dev/null +++ b/queue-6.12/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch @@ -0,0 +1,78 @@ +From ea2b96ebf39e38c225ecc081f9184d19088b8d73 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:39:01 -0700 +Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap + +From: Zack McKevitt + +[ Upstream commit aa16b2bc0f02709919e2435f531406531e5bcc69 ] + +The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to +(re)mapping beyond the VMA if the BO is too large. This can cause use +after free issues when munmap() unmaps only the VMA region and not the +additional mappings. To prevent this, check the remaining size of the +VMA before remapping and truncate the remapped length if sg->length is +too large. + +Reported-by: Lukas Maar +Fixes: ff13be830333 ("accel/qaic: Add datapath") +Reviewed-by: Karol Wachowski +Signed-off-by: Zack McKevitt +Reviewed-by: Jeff Hugo +[jhugo: fix braces from checkpatch --strict] +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_data.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c +index 265eeb4e156fc..aa89571b37f0e 100644 +--- a/drivers/accel/qaic/qaic_data.c ++++ b/drivers/accel/qaic/qaic_data.c +@@ -605,8 +605,11 @@ static const struct vm_operations_struct drm_vm_ops = { + static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) + { + struct qaic_bo *bo = to_qaic_bo(obj); ++ unsigned long remap_start; + unsigned long offset = 0; ++ unsigned long remap_end; + struct scatterlist *sg; ++ unsigned long length; + int ret = 0; + + if (obj->import_attach) +@@ -614,11 +617,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc + + for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { + if (sg_page(sg)) { ++ /* if sg is too large for the VMA, so truncate it to fit */ ++ if (check_add_overflow(vma->vm_start, offset, &remap_start)) ++ return -EINVAL; ++ if (check_add_overflow(remap_start, sg->length, &remap_end)) ++ return -EINVAL; ++ ++ if (remap_end > vma->vm_end) { ++ if (check_sub_overflow(vma->vm_end, remap_start, &length)) ++ return -EINVAL; ++ } else { ++ length = sg->length; ++ } ++ ++ if (length == 0) ++ goto out; ++ + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), +- sg->length, vma->vm_page_prot); ++ length, vma->vm_page_prot); + if (ret) + goto out; +- offset += sg->length; ++ offset += length; + } + } + +-- +2.53.0 + diff --git a/queue-6.12/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch b/queue-6.12/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch new file mode 100644 index 0000000000..1e3b17b844 --- /dev/null +++ b/queue-6.12/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch @@ -0,0 +1,48 @@ +From 9e122e421f7c58d99b444282d2db2b4c96440a72 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:12:38 +0800 +Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node + +From: Shuhao Fu + +[ Upstream commit fca7401fe37f7abc6e54147ea560f37279231137 ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers must balance it with acpi_dev_put(). + +cs35l41_hda_read_acpi() stores the returned ACPI device in +cs35l41->dacpi. That reference is normally released by the later +probe cleanup or the remove path, but the NULL-check on +physdev exits before either of those paths can run. + +Drop the lookup reference before returning -ENODEV. + +Fixes: c34b04cc6178 ("ALSA: hda: cs35l41: Fix NULL pointer dereference in cs35l41_hda_read_acpi()") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/pci/hda/cs35l41_hda.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c +index e115b9bd7ce3d..42c576d9f1179 100644 +--- a/sound/pci/hda/cs35l41_hda.c ++++ b/sound/pci/hda/cs35l41_hda.c +@@ -1865,8 +1865,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i + + cs35l41->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); +- if (!physdev) ++ if (!physdev) { ++ acpi_dev_put(adev); + return -ENODEV; ++ } + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) +-- +2.53.0 + diff --git a/queue-6.12/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch b/queue-6.12/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch new file mode 100644 index 0000000000..ed969d6d8a --- /dev/null +++ b/queue-6.12/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch @@ -0,0 +1,44 @@ +From 5278cc6f99764158e07430ad600b9cd6c75b84b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:01:39 +0800 +Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion + +From: Shuhao Fu + +[ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers are expected to balance it with acpi_dev_put(). + +When no companion is already attached, cs35l56_hda_read_acpi() looks +up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves +the lookup reference held. + +ACPI_COMPANION_SET() does not take ownership of that reference, so +drop it with acpi_dev_put() after attaching the companion. + +Fixes: 73cfbfa9caea ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/pci/hda/cs35l56_hda.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c +index ee5387140ae48..5c0c4b79f5197 100644 +--- a/sound/pci/hda/cs35l56_hda.c ++++ b/sound/pci/hda/cs35l56_hda.c +@@ -953,6 +953,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) + return -ENODEV; + } + ACPI_COMPANION_SET(cs35l56->base.dev, adev); ++ acpi_dev_put(adev); + } + + /* Initialize things that could be overwritten by a fixup */ +-- +2.53.0 + diff --git a/queue-6.12/alsa-scarlett2-add-missing-error-check-when-initiali.patch b/queue-6.12/alsa-scarlett2-add-missing-error-check-when-initiali.patch new file mode 100644 index 0000000000..99aa4419b2 --- /dev/null +++ b/queue-6.12/alsa-scarlett2-add-missing-error-check-when-initiali.patch @@ -0,0 +1,41 @@ +From 68d9498099058a6c74cbdab20666ba8357a1b8e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 10:39:14 +0700 +Subject: ALSA: scarlett2: Add missing error check when initialise Autogain + Status + +From: Robertus Diawan Chris + +[ Upstream commit c0e4fffc0f474b7ed10adee4ab2bc1a66d36fc72 ] + +When initialise new control with scarlett2_add_new_ctl() function for +Autogain Status, scarlett2_add_new_ctl() might throw an error. So, add +error check after initialise new control for Autogain Status. + +This is reported by Coverity Scan with CID 1598781 as UNUSED_VALUE. + +Fixes: 0a995e38dc44 ("ALSA: scarlett2: Add support for software-controllable input gain") +Signed-off-by: Robertus Diawan Chris +Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer_scarlett2.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c +index ef5945aa40e4a..d767a89e452d1 100644 +--- a/sound/usb/mixer_scarlett2.c ++++ b/sound/usb/mixer_scarlett2.c +@@ -6956,6 +6956,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_status_ctl, + i, 1, s, &private->autogain_status_ctls[i]); ++ if (err < 0) ++ return err; + } + + /* Add autogain target controls */ +-- +2.53.0 + diff --git a/queue-6.12/alsa-seq-serialize-ump-output-teardown-with-event_in.patch b/queue-6.12/alsa-seq-serialize-ump-output-teardown-with-event_in.patch new file mode 100644 index 0000000000..7dd679e45b --- /dev/null +++ b/queue-6.12/alsa-seq-serialize-ump-output-teardown-with-event_in.patch @@ -0,0 +1,174 @@ +From 1f3616f67d39f16ee2e6e4ab74ec72221a61da9d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:32:49 +0800 +Subject: ALSA: seq: Serialize UMP output teardown with event_input + +From: Zhang Cen + +[ Upstream commit 60a1969fae6209644698fca91c185d153674f631 ] + +seq_ump_process_event() borrows client->out_rfile.output without +synchronizing with the first-open and last-close transition in +seq_ump_client_open() and seq_ump_client_close(). + +The last output unuse can therefore drop opened[STR_OUT] to zero and +release the rawmidi file while an in-flight event_input callback is still +inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream +runtime exposed to teardown before the write path has taken its own +buffer reference. + +Add a per-client rwlock for the event_input-visible output file. Publish +a newly opened output file under the write side, and hold the read side +from the output lookup through snd_rawmidi_kernel_write(). The last +output close copies and clears the visible output file under the write +side, then drops the lock and releases the saved rawmidi file. Use +IRQ-safe rwlock guards because event_input can also be reached from +atomic sequencer delivery. + +The buggy scenario involves two paths, with each column showing the +order within that path: + +path A label: event_input path path B label: last unuse path +1. seq_ump_process_event() reads 1. seq_ump_client_close() + client->out_rfile.output. drops opened[STR_OUT] to zero. +2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release() + has not yet pinned runtime. closes the output file. +3. The writer continues using 3. close_substream() frees + the borrowed substream. substream->runtime. + +This keeps the output substream and runtime alive for the full +event_input write while keeping rawmidi release outside the rwlock. + +KASAN reproduced this as a slab-use-after-free in +snd_rawmidi_kernel_write1(), with allocation through +seq_ump_use()/snd_seq_port_connect() and free through +seq_ump_unuse()/snd_seq_port_disconnect(). + +Suggested-by: Takashi Iwai + +Validation reproduced this kernel report: +KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400 +RIP: 0033:0x7f5528af837f +Read of size 8 +Call trace: + dump_stack_lvl+0x73/0xb0 (?:?) + print_report+0xd1/0x650 (?:?) + srso_alias_return_thunk+0x5/0xfbef5 (?:?) + __virt_addr_valid+0x1a7/0x340 (?:?) + kasan_complete_mode_report_info+0x64/0x200 (?:?) + kasan_report+0xf7/0x130 (?:?) + snd_rawmidi_kernel_write1+0x9d/0x400 (?:?) + __asan_load8+0x82/0xb0 (?:?) + update_stack_state+0x1ef/0x2d0 (?:?) + snd_rawmidi_kernel_write+0x1a/0x20 (?:?) + seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82) + __snd_seq_deliver_single_event+0x8a/0xe0 (?:?) + snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?) + lock_acquire+0x14e/0x2e0 (?:?) + find_held_lock+0x31/0x90 (?:?) + snd_seq_port_use_ptr+0xa6/0xe0 (?:?) + __kasan_check_write+0x18/0x20 (?:?) + do_raw_read_unlock+0x32/0xa0 (?:?) + _raw_read_unlock+0x26/0x50 (?:?) + snd_seq_deliver_single_event+0x45c/0x4b0 (?:?) + snd_seq_deliver_event+0x10d/0x1b0 (?:?) + snd_seq_client_enqueue_event+0x192/0x240 (?:?) + snd_seq_write+0x2cd/0x450 (?:?) + apparmor_file_permission+0x20/0x30 (?:?) + security_file_permission+0x51/0x60 (?:?) + vfs_write+0x1ce/0x850 (?:?) + __fget_files+0x12b/0x220 (?:?) + lock_release+0xc8/0x2a0 (?:?) + __rcu_read_unlock+0x74/0x2d0 (?:?) + __fget_files+0x135/0x220 (?:?) + ksys_write+0x15a/0x180 (?:?) + rcu_is_watching+0x24/0x60 (?:?) + __x64_sys_write+0x46/0x60 (?:?) + x64_sys_call+0x7d/0x20d0 (?:?) + do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) + entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) + +Fixes: 81fd444aa371 ("ALSA: seq: Bind UMP device") +Signed-off-by: Zhang Cen +Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/core/seq/seq_ump_client.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c +index d39cea7f341d4..0d5d5ade87e68 100644 +--- a/sound/core/seq/seq_ump_client.c ++++ b/sound/core/seq/seq_ump_client.c +@@ -37,6 +37,7 @@ struct seq_ump_client { + struct snd_ump_endpoint *ump; /* assigned endpoint */ + int seq_client; /* sequencer client id */ + int opened[2]; /* current opens for each direction */ ++ rwlock_t output_lock; /* protects out_rfile output access */ + struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + struct seq_ump_input_buffer input; /* input parser context */ + void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ +@@ -88,6 +89,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + unsigned char type; + int len; + ++ guard(read_lock_irqsave)(&client->output_lock); + substream = client->out_rfile.output; + if (!substream) + return -ENODEV; +@@ -106,6 +108,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + static int seq_ump_client_open(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + int err; + + guard(mutex)(&ump->open_mutex); +@@ -113,9 +116,11 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + err = snd_rawmidi_kernel_open(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, +- &client->out_rfile); ++ &rfile); + if (err < 0) + return err; ++ scoped_guard(write_lock_irqsave, &client->output_lock) ++ client->out_rfile = rfile; + } + client->opened[dir]++; + return 0; +@@ -125,11 +130,19 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + static int seq_ump_client_close(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + + guard(mutex)(&ump->open_mutex); +- if (!--client->opened[dir]) +- if (dir == STR_OUT) +- snd_rawmidi_kernel_release(&client->out_rfile); ++ if (!--client->opened[dir]) { ++ if (dir == STR_OUT) { ++ scoped_guard(write_lock_irqsave, &client->output_lock) { ++ rfile = client->out_rfile; ++ client->out_rfile = (struct snd_rawmidi_file){}; ++ } ++ if (rfile.rmidi) ++ snd_rawmidi_kernel_release(&rfile); ++ } ++ } + return 0; + } + +@@ -432,6 +445,7 @@ static int snd_seq_ump_probe(struct device *_dev) + + INIT_WORK(&client->group_notify_work, handle_group_notify); + client->ump = ump; ++ rwlock_init(&client->output_lock); + + client->seq_client = + snd_seq_create_kernel_client(card, ump->core.device, +-- +2.53.0 + diff --git a/queue-6.12/arm-dts-renesas-genmai-drop-superfluous-cells.patch b/queue-6.12/arm-dts-renesas-genmai-drop-superfluous-cells.patch new file mode 100644 index 0000000000..b44afcbb9f --- /dev/null +++ b/queue-6.12/arm-dts-renesas-genmai-drop-superfluous-cells.patch @@ -0,0 +1,40 @@ +From 24bf10d832471b63011826debae3e7e73021c78d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:10 +0100 +Subject: ARM: dts: renesas: genmai: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit 714e1d6bba0e0abe5c87c8e189a35fa690540df4 ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-genmai.dts:28.17-55.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +index 28e703e0f152b..9bfbd25b25a24 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +@@ -38,9 +38,6 @@ flash@18000000 { + clocks = <&mstp9_clks R7S72100_CLK_SPIBSC0>; + power-domains = <&cpg_clocks>; + +- #address-cells = <1>; +- #size-cells = <1>; +- + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; +-- +2.53.0 + diff --git a/queue-6.12/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch b/queue-6.12/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch new file mode 100644 index 0000000000..b12e3261d5 --- /dev/null +++ b/queue-6.12/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch @@ -0,0 +1,39 @@ +From 0992052c1db4d7a6bffa43a069a3a159011f2e24 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:11 +0100 +Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit ab83176d3cf1cf1c1f6e604432905bda4515d17f ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts:32.17-72.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +index b547216d48014..416c741034482 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +@@ -36,8 +36,6 @@ flash@18000000 { + power-domains = <&cpg_clocks>; + bank-width = <4>; + device-width = <1>; +- #address-cells = <1>; +- #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; +-- +2.53.0 + diff --git a/queue-6.12/arm-integrator-fix-early-initialization.patch b/queue-6.12/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..15a2aa40dc --- /dev/null +++ b/queue-6.12/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From 7a3af1a56f3ed7ceaa3c57f191143f36a1901426 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c +index 2ed4ded56b3fe..03dfb5f720b7b 100644 +--- a/arch/arm/mach-versatile/integrator_cp.c ++++ b/arch/arm/mach-versatile/integrator_cp.c +@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-6.12/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch b/queue-6.12/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch new file mode 100644 index 0000000000..ef6cf074e4 --- /dev/null +++ b/queue-6.12/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch @@ -0,0 +1,48 @@ +From ed75a483951fd436115ad2b197d642eb10235442 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:30:57 +0100 +Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove() + +From: Richard Fitzgerald + +[ Upstream commit 18e7bd9f2446664053f8c34b72abd4606d22d858 ] + +Use flush_work() instead of cancel_work_sync() to terminate pending IRQ +work in cs35l56_sdw_remove(). And flush_work() again after masking the +interrupts to flush any queueing that was racing with the masking. This is +the same sequence as cs35l56_sdw_system_suspend(). + +cs35l56_sdw_interrupt() takes the pm_runtime to prevent the bus powering- +down before the interrupt status can be read and handled. The work releases +this pm_runtime. So cancelling it, instead of flushing, could leave an +unbalanced pm_runtime. + +Signed-off-by: Richard Fitzgerald +Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") +Link: https://patch.msgid.link/20260521123057.988732-1-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs35l56-sdw.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +index 7c9a17fe2195c..b9cc447b6e5c7 100644 +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -544,10 +544,11 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; +- cancel_work_sync(&cs35l56->sdw_irq_work); ++ flush_work(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ flush_work(&cs35l56->sdw_irq_work); + + cs35l56_remove(cs35l56); + +-- +2.53.0 + diff --git a/queue-6.12/blk-integrity-enable-p2p-source-and-destination.patch b/queue-6.12/blk-integrity-enable-p2p-source-and-destination.patch new file mode 100644 index 0000000000..6fb630cec1 --- /dev/null +++ b/queue-6.12/blk-integrity-enable-p2p-source-and-destination.patch @@ -0,0 +1,89 @@ +From 5690c8c77a8259052708f47fa401e4ef6bcae0cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 3 Sep 2025 12:33:16 -0700 +Subject: blk-integrity: enable p2p source and destination + +From: Keith Busch + +[ Upstream commit 05ceea5d3ec9a1b1d6858ffd4739fdb0ed1b8eaf ] + +Set the extraction flags to allow p2p pages for the metadata buffer if +the block device allows it. Similar to data payloads, ensure the bio +does not use merging if we see a p2p page. + +Reviewed-by: Christoph Hellwig +Reviewed-by: Martin K. Petersen +Signed-off-by: Keith Busch +Signed-off-by: Jens Axboe +Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()") +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index a7788bbe35979..2a02222f4298c 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -268,7 +268,8 @@ static int bio_integrity_init_user(struct bio *bio, struct bio_vec *bvec, + } + + static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages, +- int nr_vecs, ssize_t bytes, ssize_t offset) ++ int nr_vecs, ssize_t bytes, ssize_t offset, ++ bool *is_p2p) + { + unsigned int nr_bvecs = 0; + int i, j; +@@ -289,6 +290,9 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages, + bytes -= next; + } + ++ if (is_pci_p2pdma_page(pages[i])) ++ *is_p2p = true; ++ + bvec_set_page(&bvec[nr_bvecs], pages[i], size, offset); + offset = 0; + nr_bvecs++; +@@ -302,10 +306,11 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + struct request_queue *q = bdev_get_queue(bio->bi_bdev); + struct page *stack_pages[UIO_FASTIOV], **pages = stack_pages; + struct bio_vec stack_vec[UIO_FASTIOV], *bvec = stack_vec; ++ iov_iter_extraction_t extraction_flags = 0; + size_t offset, bytes = iter->count; ++ bool copy, is_p2p = false; + unsigned int nr_bvecs; + int ret, nr_vecs; +- bool copy; + + if (bio_integrity(bio)) + return -EINVAL; +@@ -324,15 +329,23 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + + copy = iov_iter_alignment(iter) & + blk_lim_dma_alignment_and_pad(&q->limits); +- ret = iov_iter_extract_pages(iter, &pages, bytes, nr_vecs, 0, &offset); ++ ++ if (blk_queue_pci_p2pdma(q)) ++ extraction_flags |= ITER_ALLOW_P2PDMA; ++ ++ ret = iov_iter_extract_pages(iter, &pages, bytes, nr_vecs, ++ extraction_flags, &offset); + if (unlikely(ret < 0)) + goto free_bvec; + +- nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset); ++ nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, ++ &is_p2p); + if (pages != stack_pages) + kvfree(pages); + if (nr_bvecs > queue_max_integrity_segments(q)) + copy = true; ++ if (is_p2p) ++ bio->bi_opf |= REQ_NOMERGE; + + if (copy) + ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes); +-- +2.53.0 + diff --git a/queue-6.12/blk-integrity-remove-seed-for-user-mapped-buffers.patch b/queue-6.12/blk-integrity-remove-seed-for-user-mapped-buffers.patch new file mode 100644 index 0000000000..b5b48577d8 --- /dev/null +++ b/queue-6.12/blk-integrity-remove-seed-for-user-mapped-buffers.patch @@ -0,0 +1,230 @@ +From 9b6e897af3a5bd258b6660caee81eb567293d0ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 16 Oct 2024 13:13:09 -0700 +Subject: blk-integrity: remove seed for user mapped buffers + +From: Keith Busch + +[ Upstream commit 133008e84b99e4f5f8cf3d8b768c995732df9406 ] + +The seed is only used for kernel generation and verification. That +doesn't happen for user buffers, so passing the seed around doesn't +accomplish anything. + +Signed-off-by: Keith Busch +Reviewed-by: Christoph Hellwig +Reviewed-by: Anuj Gupta +Reviewed-by: Kanchan Joshi +Link: https://lore.kernel.org/r/20241016201309.1090320-1-kbusch@meta.com +Signed-off-by: Jens Axboe +Stable-dep-of: 637ad3a56a3b ("block: don't overwrite bip_vcnt in bio_integrity_copy_user()") +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 13 +++++-------- + block/blk-integrity.c | 4 ++-- + drivers/nvme/host/ioctl.c | 17 ++++++++--------- + include/linux/bio-integrity.h | 4 ++-- + include/linux/blk-integrity.h | 5 ++--- + 5 files changed, 19 insertions(+), 24 deletions(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 6641ecbf69678..ab58f44058e96 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -197,7 +197,7 @@ EXPORT_SYMBOL(bio_integrity_add_page); + + static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + int nr_vecs, unsigned int len, +- unsigned int direction, u32 seed) ++ unsigned int direction) + { + bool write = direction == ITER_SOURCE; + struct bio_integrity_payload *bip; +@@ -245,7 +245,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + } + + bip->bip_flags |= BIP_COPY_USER; +- bip->bip_iter.bi_sector = seed; + bip->bip_vcnt = nr_vecs; + return 0; + free_bip: +@@ -256,7 +255,7 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + } + + static int bio_integrity_init_user(struct bio *bio, struct bio_vec *bvec, +- int nr_vecs, unsigned int len, u32 seed) ++ int nr_vecs, unsigned int len) + { + struct bio_integrity_payload *bip; + +@@ -265,7 +264,6 @@ static int bio_integrity_init_user(struct bio *bio, struct bio_vec *bvec, + return PTR_ERR(bip); + + memcpy(bip->bip_vec, bvec, nr_vecs * sizeof(*bvec)); +- bip->bip_iter.bi_sector = seed; + bip->bip_iter.bi_size = len; + bip->bip_vcnt = nr_vecs; + return 0; +@@ -301,8 +299,7 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages, + return nr_bvecs; + } + +-int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes, +- u32 seed) ++int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes) + { + struct request_queue *q = bdev_get_queue(bio->bi_bdev); + unsigned int align = blk_lim_dma_alignment_and_pad(&q->limits); +@@ -348,9 +345,9 @@ int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes, + + if (copy) + ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes, +- direction, seed); ++ direction); + else +- ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes, seed); ++ ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes); + if (ret) + goto release_pages; + if (bvec != stack_vec) +diff --git a/block/blk-integrity.c b/block/blk-integrity.c +index 3fe0681399f6e..013469faa5e7c 100644 +--- a/block/blk-integrity.c ++++ b/block/blk-integrity.c +@@ -113,9 +113,9 @@ int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist) + EXPORT_SYMBOL(blk_rq_map_integrity_sg); + + int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, +- ssize_t bytes, u32 seed) ++ ssize_t bytes) + { +- int ret = bio_integrity_map_user(rq->bio, ubuf, bytes, seed); ++ int ret = bio_integrity_map_user(rq->bio, ubuf, bytes); + + if (ret) + return ret; +diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c +index 64ae8af01d9a4..930521c633d23 100644 +--- a/drivers/nvme/host/ioctl.c ++++ b/drivers/nvme/host/ioctl.c +@@ -114,7 +114,7 @@ static struct request *nvme_alloc_user_request(struct request_queue *q, + + static int nvme_map_user_request(struct request *req, u64 ubuffer, + unsigned bufflen, void __user *meta_buffer, unsigned meta_len, +- u32 meta_seed, struct io_uring_cmd *ioucmd, unsigned int flags) ++ struct io_uring_cmd *ioucmd, unsigned int flags) + { + struct request_queue *q = req->q; + struct nvme_ns *ns = q->queuedata; +@@ -164,8 +164,7 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + bio_set_dev(bio, bdev); + + if (has_metadata) { +- ret = blk_rq_integrity_map_user(req, meta_buffer, meta_len, +- meta_seed); ++ ret = blk_rq_integrity_map_user(req, meta_buffer, meta_len); + if (ret) + goto out_unmap; + } +@@ -182,7 +181,7 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + + static int nvme_submit_user_cmd(struct request_queue *q, + struct nvme_command *cmd, u64 ubuffer, unsigned bufflen, +- void __user *meta_buffer, unsigned meta_len, u32 meta_seed, ++ void __user *meta_buffer, unsigned meta_len, + u64 *result, unsigned timeout, unsigned int flags) + { + struct nvme_ns *ns = q->queuedata; +@@ -199,7 +198,7 @@ static int nvme_submit_user_cmd(struct request_queue *q, + req->timeout = timeout; + if (ubuffer && bufflen) { + ret = nvme_map_user_request(req, ubuffer, bufflen, meta_buffer, +- meta_len, meta_seed, NULL, flags); ++ meta_len, NULL, flags); + if (ret) + return ret; + } +@@ -280,7 +279,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) + c.rw.lbatm = cpu_to_le16(io.appmask); + + return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata, +- meta_len, lower_32_bits(io.slba), NULL, 0, 0); ++ meta_len, NULL, 0, 0); + } + + static bool nvme_validate_passthru_nsid(struct nvme_ctrl *ctrl, +@@ -334,7 +333,7 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns, + + status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c, + cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata), +- cmd.metadata_len, 0, &result, timeout, 0); ++ cmd.metadata_len, &result, timeout, 0); + + if (status >= 0) { + if (put_user(result, &ucmd->result)) +@@ -381,7 +380,7 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns, + + status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c, + cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata), +- cmd.metadata_len, 0, &cmd.result, timeout, flags); ++ cmd.metadata_len, &cmd.result, timeout, flags); + + if (status >= 0) { + if (put_user(cmd.result, &ucmd->result)) +@@ -511,7 +510,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns, + if (d.addr && d.data_len) { + ret = nvme_map_user_request(req, d.addr, + d.data_len, nvme_to_user_ptr(d.metadata), +- d.metadata_len, 0, ioucmd, vec); ++ d.metadata_len, ioucmd, vec); + if (ret) + return ret; + } +diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h +index dd831c269e994..dbf0f74c15291 100644 +--- a/include/linux/bio-integrity.h ++++ b/include/linux/bio-integrity.h +@@ -72,7 +72,7 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp, + unsigned int nr); + int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len, + unsigned int offset); +-int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t len, u32 seed); ++int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t len); + void bio_integrity_unmap_user(struct bio *bio); + bool bio_integrity_prep(struct bio *bio); + void bio_integrity_advance(struct bio *bio, unsigned int bytes_done); +@@ -99,7 +99,7 @@ static inline void bioset_integrity_free(struct bio_set *bs) + } + + static inline int bio_integrity_map_user(struct bio *bio, void __user *ubuf, +- ssize_t len, u32 seed) ++ ssize_t len) + { + return -EINVAL; + } +diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h +index 676f8f860c474..c7eae0bfb013f 100644 +--- a/include/linux/blk-integrity.h ++++ b/include/linux/blk-integrity.h +@@ -28,7 +28,7 @@ static inline bool queue_limits_stack_integrity_bdev(struct queue_limits *t, + int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); + int blk_rq_count_integrity_sg(struct request_queue *, struct bio *); + int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, +- ssize_t bytes, u32 seed); ++ ssize_t bytes); + + static inline bool + blk_integrity_queue_supports_integrity(struct request_queue *q) +@@ -104,8 +104,7 @@ static inline int blk_rq_map_integrity_sg(struct request *q, + } + static inline int blk_rq_integrity_map_user(struct request *rq, + void __user *ubuf, +- ssize_t bytes, +- u32 seed) ++ ssize_t bytes) + { + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/blk-integrity-use-simpler-alignment-check.patch b/queue-6.12/blk-integrity-use-simpler-alignment-check.patch new file mode 100644 index 0000000000..1502c96ddc --- /dev/null +++ b/queue-6.12/blk-integrity-use-simpler-alignment-check.patch @@ -0,0 +1,48 @@ +From bcdad12583153fdfa3752142a5e540d1ce21e4d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 27 Aug 2025 07:12:57 -0700 +Subject: blk-integrity: use simpler alignment check + +From: Keith Busch + +[ Upstream commit 69d7ed5b9ef661230264bfa0db4c96fa25b8efa4 ] + +We're checking length and addresses against the same alignment value, so +use the more simple iterator check. + +Signed-off-by: Keith Busch +Reviewed-by: Hannes Reinecke +Reviewed-by: Martin K. Petersen +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()") +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 6801754838bc1..a7788bbe35979 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -300,7 +300,6 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages, + int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + { + struct request_queue *q = bdev_get_queue(bio->bi_bdev); +- unsigned int align = blk_lim_dma_alignment_and_pad(&q->limits); + struct page *stack_pages[UIO_FASTIOV], **pages = stack_pages; + struct bio_vec stack_vec[UIO_FASTIOV], *bvec = stack_vec; + size_t offset, bytes = iter->count; +@@ -323,7 +322,8 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + pages = NULL; + } + +- copy = !iov_iter_is_aligned(iter, align, align); ++ copy = iov_iter_alignment(iter) & ++ blk_lim_dma_alignment_and_pad(&q->limits); + ret = iov_iter_extract_pages(iter, &pages, bytes, nr_vecs, 0, &offset); + if (unlikely(ret < 0)) + goto free_bvec; +-- +2.53.0 + diff --git a/queue-6.12/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch b/queue-6.12/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch new file mode 100644 index 0000000000..c9712cbfa5 --- /dev/null +++ b/queue-6.12/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch @@ -0,0 +1,72 @@ +From f1405c7cc535726df5fafe7c30913fd28b076b6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 01:09:29 -0400 +Subject: block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user() + +From: Sungwoo Kim + +[ Upstream commit 8582792cf23b3d94674d4d838f7cde9a28d0fcaf ] + +pin_user_pages_fast() can partially succeed and return the number of +pages that were actually pinned. However, the bio_integrity_map_user() +does not handle this partial pinning. This leads to a general protection +fault since bvec_from_pages() dereferences an unpinned page address, +which is 0. + +To fix this, add a check to verify that all requested memory is pinned. +If partial pinning occurs, unpin the memory and return -EFAULT. + +Kernel Oops: + +Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy) +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 +RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6 + +Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers") +Acked-by: Chao Shi +Acked-by: Weidong Zhu +Acked-by: Dave Tian +Signed-off-by: Sungwoo Kim +Tested-by: Shin'ichiro Kawasaki +Link: https://github.com/linux-blktests/blktests/pull/244 +Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 2a02222f4298c..04bab987b0878 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -338,6 +338,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + if (unlikely(ret < 0)) + goto free_bvec; + ++ /* ++ * Handle partial pinning. This can happen when pin_user_pages_fast() ++ * returns fewer pages than requested. ++ */ ++ if (user_backed_iter(iter) && unlikely(ret != bytes)) { ++ if (ret > 0) { ++ int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ int i; ++ ++ for (i = 0; i < npinned; i++) ++ unpin_user_page(pages[i]); ++ } ++ if (pages != stack_pages) ++ kvfree(pages); ++ ret = -EFAULT; ++ goto free_bvec; ++ } ++ + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, + &is_p2p); + if (pages != stack_pages) +-- +2.53.0 + diff --git a/queue-6.12/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch b/queue-6.12/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch new file mode 100644 index 0000000000..e1e4e29ce8 --- /dev/null +++ b/queue-6.12/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch @@ -0,0 +1,43 @@ +From 5bef07db158f5ff44b72465827e6208569b5e677 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 22:51:51 +0100 +Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user() + +From: David Carlier + +[ Upstream commit 637ad3a56a3b889527d1dacea6fea2a8bd648140 ] + +bio_integrity_add_page() already sets bip_vcnt to 1 for the bounce +segment. Overwriting it with nr_vecs breaks bip_vcnt <= bip_max_vcnt +on WRITE (bip_max_vcnt is 1), so the gap-merge checks in block/blk.h +read past the bip_vec[] flex array. On READ the read is in bounds +but lands on a saved user bvec instead of the bounce. + +The line was added for split propagation, but bio_integrity_clone() +doesn't copy bip_vcnt and BIP_CLONE_FLAGS excludes BIP_COPY_USER. + +Fixes: 3991657ae707 ("block: set bip_vcnt correctly") +Signed-off-by: David Carlier +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index ab58f44058e96..9c490fa07a795 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -245,7 +245,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + } + + bip->bip_flags |= BIP_COPY_USER; +- bip->bip_vcnt = nr_vecs; + return 0; + free_bip: + bio_integrity_free(bio); +-- +2.53.0 + diff --git a/queue-6.12/block-drop-direction-param-from-bio_integrity_copy_u.patch b/queue-6.12/block-drop-direction-param-from-bio_integrity_copy_u.patch new file mode 100644 index 0000000000..1d2bdf9046 --- /dev/null +++ b/queue-6.12/block-drop-direction-param-from-bio_integrity_copy_u.patch @@ -0,0 +1,84 @@ +From 2a4f486970ac0fbddde1b4a886923e2701b12a8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 3 Jun 2025 12:31:32 -0600 +Subject: block: drop direction param from bio_integrity_copy_user() + +From: Caleb Sander Mateos + +[ Upstream commit c09a8b00f850d3ca0af998bff1fac4a3f6d11768 ] + +direction is determined from bio, which is already passed in. Compute +op_is_write(bio_op(bio)) directly instead of converting it to an iter +direction and back to a bool. + +Signed-off-by: Caleb Sander Mateos +Reviewed-by: Keith Busch +Reviewed-by: Anuj Gupta +Link: https://lore.kernel.org/r/20250603183133.1178062-1-csander@purestorage.com +Signed-off-by: Jens Axboe +Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()") +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 785adefc5f3c3..6801754838bc1 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -196,10 +196,9 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, + EXPORT_SYMBOL(bio_integrity_add_page); + + static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, +- int nr_vecs, unsigned int len, +- unsigned int direction) ++ int nr_vecs, unsigned int len) + { +- bool write = direction == ITER_SOURCE; ++ bool write = op_is_write(bio_op(bio)); + struct bio_integrity_payload *bip; + struct iov_iter iter; + void *buf; +@@ -210,7 +209,7 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + return -ENOMEM; + + if (write) { +- iov_iter_bvec(&iter, direction, bvec, nr_vecs, len); ++ iov_iter_bvec(&iter, ITER_SOURCE, bvec, nr_vecs, len); + if (!copy_from_iter_full(buf, len, &iter)) { + ret = -EFAULT; + goto free_buf; +@@ -305,7 +304,7 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + struct page *stack_pages[UIO_FASTIOV], **pages = stack_pages; + struct bio_vec stack_vec[UIO_FASTIOV], *bvec = stack_vec; + size_t offset, bytes = iter->count; +- unsigned int direction, nr_bvecs; ++ unsigned int nr_bvecs; + int ret, nr_vecs; + bool copy; + +@@ -314,11 +313,6 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + if (bytes >> SECTOR_SHIFT > queue_max_hw_sectors(q)) + return -E2BIG; + +- if (bio_data_dir(bio) == READ) +- direction = ITER_DEST; +- else +- direction = ITER_SOURCE; +- + nr_vecs = iov_iter_npages(iter, BIO_MAX_VECS + 1); + if (nr_vecs > BIO_MAX_VECS) + return -E2BIG; +@@ -341,8 +335,7 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + copy = true; + + if (copy) +- ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes, +- direction); ++ ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes); + else + ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes); + if (ret) +-- +2.53.0 + diff --git a/queue-6.12/block-modify-bio_integrity_map_user-to-accept-iov_it.patch b/queue-6.12/block-modify-bio_integrity_map_user-to-accept-iov_it.patch new file mode 100644 index 0000000000..fa5500a00a --- /dev/null +++ b/queue-6.12/block-modify-bio_integrity_map_user-to-accept-iov_it.patch @@ -0,0 +1,118 @@ +From 01addf13679f46fe50e191e39e2f230bacc5f3ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 Nov 2024 16:52:33 +0530 +Subject: block: modify bio_integrity_map_user to accept iov_iter as argument + +From: Anuj Gupta + +[ Upstream commit fe8f4ca7107e968b0eb7328155c8811f2a19424a ] + +This patch refactors bio_integrity_map_user to accept iov_iter as +argument. This is a prep patch. + +Signed-off-by: Anuj Gupta +Signed-off-by: Kanchan Joshi +Reviewed-by: Christoph Hellwig +Reviewed-by: Keith Busch +Link: https://lore.kernel.org/r/20241128112240.8867-4-anuj20.g@samsung.com +Signed-off-by: Jens Axboe +Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()") +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 12 +++++------- + block/blk-integrity.c | 10 +++++++++- + include/linux/bio-integrity.h | 5 ++--- + 3 files changed, 16 insertions(+), 11 deletions(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 9c490fa07a795..785adefc5f3c3 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -298,16 +298,15 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages, + return nr_bvecs; + } + +-int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes) ++int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + { + struct request_queue *q = bdev_get_queue(bio->bi_bdev); + unsigned int align = blk_lim_dma_alignment_and_pad(&q->limits); + struct page *stack_pages[UIO_FASTIOV], **pages = stack_pages; + struct bio_vec stack_vec[UIO_FASTIOV], *bvec = stack_vec; ++ size_t offset, bytes = iter->count; + unsigned int direction, nr_bvecs; +- struct iov_iter iter; + int ret, nr_vecs; +- size_t offset; + bool copy; + + if (bio_integrity(bio)) +@@ -320,8 +319,7 @@ int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes) + else + direction = ITER_SOURCE; + +- iov_iter_ubuf(&iter, direction, ubuf, bytes); +- nr_vecs = iov_iter_npages(&iter, BIO_MAX_VECS + 1); ++ nr_vecs = iov_iter_npages(iter, BIO_MAX_VECS + 1); + if (nr_vecs > BIO_MAX_VECS) + return -E2BIG; + if (nr_vecs > UIO_FASTIOV) { +@@ -331,8 +329,8 @@ int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t bytes) + pages = NULL; + } + +- copy = !iov_iter_is_aligned(&iter, align, align); +- ret = iov_iter_extract_pages(&iter, &pages, bytes, nr_vecs, 0, &offset); ++ copy = !iov_iter_is_aligned(iter, align, align); ++ ret = iov_iter_extract_pages(iter, &pages, bytes, nr_vecs, 0, &offset); + if (unlikely(ret < 0)) + goto free_bvec; + +diff --git a/block/blk-integrity.c b/block/blk-integrity.c +index 013469faa5e7c..a1678f0a9f81f 100644 +--- a/block/blk-integrity.c ++++ b/block/blk-integrity.c +@@ -115,8 +115,16 @@ EXPORT_SYMBOL(blk_rq_map_integrity_sg); + int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, + ssize_t bytes) + { +- int ret = bio_integrity_map_user(rq->bio, ubuf, bytes); ++ int ret; ++ struct iov_iter iter; ++ unsigned int direction; + ++ if (op_is_write(req_op(rq))) ++ direction = ITER_DEST; ++ else ++ direction = ITER_SOURCE; ++ iov_iter_ubuf(&iter, direction, ubuf, bytes); ++ ret = bio_integrity_map_user(rq->bio, &iter); + if (ret) + return ret; + +diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h +index dbf0f74c15291..be91479b2c42d 100644 +--- a/include/linux/bio-integrity.h ++++ b/include/linux/bio-integrity.h +@@ -72,7 +72,7 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp, + unsigned int nr); + int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len, + unsigned int offset); +-int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t len); ++int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter); + void bio_integrity_unmap_user(struct bio *bio); + bool bio_integrity_prep(struct bio *bio); + void bio_integrity_advance(struct bio *bio, unsigned int bytes_done); +@@ -98,8 +98,7 @@ static inline void bioset_integrity_free(struct bio_set *bs) + { + } + +-static inline int bio_integrity_map_user(struct bio *bio, void __user *ubuf, +- ssize_t len) ++static int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + { + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/block-recompute-nr_integrity_segments-in-blk_insert_.patch b/queue-6.12/block-recompute-nr_integrity_segments-in-blk_insert_.patch new file mode 100644 index 0000000000..bff038730d --- /dev/null +++ b/queue-6.12/block-recompute-nr_integrity_segments-in-blk_insert_.patch @@ -0,0 +1,82 @@ +From bf36f8c52fff797debc10f6d06c02f5fae322125 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:22:30 -0600 +Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request + +From: Casey Chen + +[ Upstream commit 2c6e6a18a37b905cb584eb0dda3ae482162a81ca ] + +blk_insert_cloned_request() already recomputes nr_phys_segments +against the bottom queue, because "the queue settings related to +segment counting may differ from the original queue." The exact same +reasoning applies to integrity segments: a stacked driver's underlying +queue can have tighter virt_boundary_mask, seg_boundary_mask, or +max_segment_size than the top queue, in which case +blk_rq_count_integrity_sg() against the bottom queue produces a +different count than the cached rq->nr_integrity_segments inherited +from the source request by blk_rq_prep_clone(). + +When the cached count is lower than the bottom queue's actual count, +blk_rq_map_integrity_sg() trips + + BUG_ON(segments > rq->nr_integrity_segments); + +on dispatch. The same families of stacked setups that motivated the +existing nr_phys_segments recompute -- dm-multipath fanning out to +nvme-rdma in particular -- can produce this. + +Mirror the nr_phys_segments handling: when the request carries +integrity, recompute nr_integrity_segments against the bottom queue +and reject the request if it exceeds the bottom queue's +max_integrity_segments. blk_rq_count_integrity_sg() and +queue_max_integrity_segments() are both already available via +, which blk-mq.c includes. + +This closes a latent gap in the stacking contract and brings the +integrity-segment accounting in line with the existing +phys-segment accounting. + +Fixes: 76c313f658d2 ("blk-integrity: improved sg segment mapping") +Signed-off-by: Casey Chen +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-mq.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 1891863dcba17..5bfaa8e4b9cf6 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -3215,6 +3215,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq) + return BLK_STS_IOERR; + } + ++ /* ++ * Integrity segment counting depends on the same queue limits ++ * (virt_boundary_mask, seg_boundary_mask, max_segment_size) that ++ * vary across stacked queues, so recompute against the bottom ++ * queue just like nr_phys_segments above. ++ */ ++ if (blk_integrity_rq(rq) && rq->bio) { ++ unsigned short max_int_segs = queue_max_integrity_segments(q); ++ ++ rq->nr_integrity_segments = ++ blk_rq_count_integrity_sg(rq->q, rq->bio); ++ if (rq->nr_integrity_segments > max_int_segs) { ++ printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n", ++ __func__, rq->nr_integrity_segments, ++ max_int_segs); ++ return BLK_STS_IOERR; ++ } ++ } ++ + if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq))) + return BLK_STS_IOERR; + +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch b/queue-6.12/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch new file mode 100644 index 0000000000..61fd4eca58 --- /dev/null +++ b/queue-6.12/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch @@ -0,0 +1,43 @@ +From f1069804a02e5ba1a4fafa13cde749b88a7b7883 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:24:02 +0800 +Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths + +From: Jiajia Liu + +[ Upstream commit dd1dda6b8d6e1f4376a5b3055a04f0ecbdb4d6bd ] + +The setup_packet of control urb is not freed if usb_submit_urb fails or +the submitted urb is killed. Add free in these two paths. + +Fixes: a1c49c434e150 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") +Signed-off-by: Jiajia Liu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index 98cb8529d8bcd..08a8c3a5d7b7d 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -496,6 +496,7 @@ static void btmtk_usb_wmt_recv(struct urb *urb) + return; + } else if (urb->status == -ENOENT) { + /* Avoid suspend failed when usb_kill_urb */ ++ kfree(urb->setup_packet); + return; + } + +@@ -569,6 +570,7 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); ++ kfree(dr); + usb_unanchor_urb(urb); + } + +-- +2.53.0 + diff --git a/queue-6.12/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch b/queue-6.12/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch new file mode 100644 index 0000000000..3384c93ebf --- /dev/null +++ b/queue-6.12/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch @@ -0,0 +1,82 @@ +From 5cea6c486d42cea2978eddabd5d5077f56a9c9cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 23:56:26 +0900 +Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx + +From: Xingwang Xiang + +[ Upstream commit ddf8029623a1af20e984c040e89ff918158397ab ] + +sk_psock_strp_data_ready() already checks tls_sw_has_ctx_rx() and +defers to psock->saved_data_ready when a TLS RX context is present, +avoiding a conflict with the TLS strparser's ownership of the receive +queue (commit e91de6afa81c, "bpf: Fix running sk_skb program types +with ktls"). + +sk_psock_verdict_data_ready() has no equivalent guard. When a socket +is inserted into a sockmap (BPF_SK_SKB_VERDICT) before TLS RX is +configured, tls_sw_strparser_arm() saves sk_psock_verdict_data_ready +as rx_ctx->saved_data_ready. On data arrival: + + tls_data_ready -> tls_strp_data_ready -> tls_rx_msg_ready + -> saved_data_ready() = sk_psock_verdict_data_ready() + -> tcp_read_skb() drains sk_receive_queue via __skb_unlink() + without calling tcp_eat_skb(), so copied_seq is not advanced. + +tls_strp_msg_load() then finds tcp_inq() >= full_len (stale), calls +tcp_recv_skb() on the now-empty queue, hits WARN_ON_ONCE(!first), and +returns with rx_ctx->strp.anchor.frag_list pointing at a psock-owned +(potentially freed) skb. tls_decrypt_sg() subsequently walks that +frag_list: use-after-free. + +Apply the same fix as sk_psock_strp_data_ready(): if a TLS RX context +is present, call psock->saved_data_ready (sock_def_readable) to wake +recv() waiters and return immediately, leaving the receive queue +untouched. TLS retains sole ownership of the queue and decrypts the +record normally through tls_sw_recvmsg(). + +Fixes: ef5659280eb1 ("bpf, sockmap: Allow skipping sk_skb parser program") +Signed-off-by: Xingwang Xiang +Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/skmsg.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/core/skmsg.c b/net/core/skmsg.c +index e1e0283e53c1d..fbaee3d09990b 100644 +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -1267,12 +1267,19 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) + static void sk_psock_verdict_data_ready(struct sock *sk) + { + const struct proto_ops *ops = NULL; ++ struct sk_psock *psock; + struct socket *sock; + int copied; + + trace_sk_data_ready(sk); + + rcu_read_lock(); ++ psock = sk_psock(sk); ++ if (psock && tls_sw_has_ctx_rx(sk)) { ++ psock->saved_data_ready(sk); ++ rcu_read_unlock(); ++ return; ++ } + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); +@@ -1282,8 +1289,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) + + copied = ops->read_skb(sk, sk_psock_verdict_recv); + if (copied >= 0) { +- struct sk_psock *psock; +- + rcu_read_lock(); + psock = sk_psock(sk); + if (psock) +-- +2.53.0 + diff --git a/queue-6.12/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-6.12/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..cd22da59ad --- /dev/null +++ b/queue-6.12/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From a417c9ac7cad030e98196f454df0100538923a70 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 3d91f5a057509..3194344529a54 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4642,10 +4642,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4653,7 +4667,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4670,7 +4683,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4678,8 +4691,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-6.12/btrfs-fix-squota-accounting-during-enable-generation.patch b/queue-6.12/btrfs-fix-squota-accounting-during-enable-generation.patch new file mode 100644 index 0000000000..131c49a030 --- /dev/null +++ b/queue-6.12/btrfs-fix-squota-accounting-during-enable-generation.patch @@ -0,0 +1,130 @@ +From 3d4961fb5b1bf67625738f227d87d8c24a2adf04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 19:53:46 -0700 +Subject: btrfs: fix squota accounting during enable generation + +From: Boris Burkov + +[ Upstream commit d7c600554816b8ef70adffe078a0e360c055d82b ] + +The first transaction that enables squotas is special and a bit tricky. +We have to set BTRFS_FS_QUOTA_ENABLED after the transaction to avoid a +deadlock, so any delayed refs that run before we set the bit are not +squota accounted. For data this is fine, we don't get an owner_ref, so +there is no real harm, it's as if the extent predated squotas. However +for metadata, the tree block will have gen == enable_gen so when we free +it later, we will decrement the squota accounting, which can result in +an underflow. Before it is freed, btrfs check shows errors, as we have +mismatched usage between the node generations/owners and the squota +values. + +There are two angles to this fix: + +1. For extents that come in delayed_refs that run during the + enable_gen transaction, we must actually set enable_gen to the *next* + transaction. That is the first transaction that we can really + properly account in any way. +2. For extents that come in between the end of our transaction handle + and the time we set the BTRFS_FS_QUOTA_ENABLED bit, we need an + additional bit, BTRFS_FS_SQUOTA_ENABLING which only affects recording + squota deltas, so we do pick up those extents. Otherwise, we would + miss them, even for enable_gen + 1. + +Fixes: bd7c1ea3a302 ("btrfs: qgroup: check generation when recording simple quota delta") +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/fs.h | 1 + + fs/btrfs/qgroup.c | 31 +++++++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 4 deletions(-) + +diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h +index 93ff1db75af48..c49ffaeee769a 100644 +--- a/fs/btrfs/fs.h ++++ b/fs/btrfs/fs.h +@@ -114,6 +114,7 @@ enum { + BTRFS_FS_LOG_RECOVERING, + BTRFS_FS_OPEN, + BTRFS_FS_QUOTA_ENABLED, ++ BTRFS_FS_SQUOTA_ENABLING, + BTRFS_FS_UPDATE_UUID_TREE_GEN, + BTRFS_FS_CREATING_FREE_SPACE_TREE, + BTRFS_FS_BTREE_ERR, +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 71ccba22752cb..5b158eb25d181 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1130,7 +1130,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + if (simple) { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE; + btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA); +- btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid); ++ /* ++ * Set the enable generation to the next transaction, as we cannot ++ * ensure that extents written during this transaction will see any ++ * state we have set here. So we should treat all extents of the ++ * transaction as coming in before squotas was enabled. ++ */ ++ btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1); + } else { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; + } +@@ -1240,7 +1246,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + goto out_free_path; + } + +- fs_info->qgroup_enable_gen = trans->transid; ++ /* ++ * Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING ++ * under the transaction handle. We want to ensure that all extents in ++ * the next transaction definitely see them. ++ */ ++ if (simple) { ++ fs_info->qgroup_enable_gen = trans->transid + 1; ++ set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ } + + mutex_unlock(&fs_info->qgroup_ioctl_lock); + /* +@@ -1254,9 +1268,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + */ + ret = btrfs_commit_transaction(trans); + trans = NULL; ++ + mutex_lock(&fs_info->qgroup_ioctl_lock); +- if (ret) ++ if (ret) { ++ if (simple) { ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ fs_info->qgroup_enable_gen = 0; ++ } + goto out_free_path; ++ } + + /* + * Set quota enabled flag after committing the transaction, to avoid +@@ -1266,6 +1286,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + spin_lock(&fs_info->qgroup_lock); + fs_info->quota_root = quota_root; + set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); ++ if (simple) ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); + spin_unlock(&fs_info->qgroup_lock); + + /* Skip rescan for simple qgroups. */ +@@ -4966,7 +4988,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info, + u64 num_bytes = delta->num_bytes; + const int sign = (delta->is_inc ? 1 : -1); + +- if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) ++ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE && ++ !test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags)) + return 0; + + if (!is_fstree(root)) +-- +2.53.0 + diff --git a/queue-6.12/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch b/queue-6.12/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch new file mode 100644 index 0000000000..6f57b1ae32 --- /dev/null +++ b/queue-6.12/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch @@ -0,0 +1,75 @@ +From e3462fa3a0e49eca9a54118084e8430f08d62b74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:58:56 +0100 +Subject: btrfs: tracepoints: fix sleep while in atomic context in + btrfs_sync_file() + +From: Filipe Manana + +[ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ] + +The trace event btrfs_sync_file() is called in an atomic context (all trace +events are) and its call to dput(), which is needed due to the call to +dget_parent(), can sleep, triggering a kernel splat. + +This can be reproduced by enabling the trace event and running btrfs/056 +from fstests for example. The splat shown in dmesg is the following: + + [53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 + [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io + [53.988] preempt_count: 2, expected: 0 + [53.967] RCU nest depth: 0, expected: 0 + [53.943] Preemption disabled at: + [53.944] [<0000000000000000>] 0x0 + [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) + [54.070] Tainted: [W]=WARN + [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 + [54.072] Call Trace: + [54.074] + [54.076] dump_stack_lvl+0x56/0x80 + [54.079] __might_resched.cold+0xd6/0x10f + [54.072] dput.part.0+0x24/0x110 + [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] + [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] + [54.087] ? __handle_mm_fault+0x8ae/0xed0 + [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] + [54.091] vfs_write+0x21f/0x450 + [54.094] __x64_sys_pwrite64+0x8d/0xc0 + [54.096] ? do_user_addr_fault+0x20c/0x670 + [54.099] do_syscall_64+0x60/0xf20 + [54.092] ? clear_bhb_loop+0x60/0xb0 + [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e + +So stop using dget_parent() and dput() and access the parent dentry +directly as dentry->d_parent. This is also what ext4 is doing in +its equivalent trace event ext4_sync_file_enter(). + +Fixes: a85b46db143f ("btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()") +Reviewed-by: Boris Burkov +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + include/trace/events/btrfs.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h +index b964cba4169c2..6382a570c76c5 100644 +--- a/include/trace/events/btrfs.h ++++ b/include/trace/events/btrfs.h +@@ -773,10 +773,8 @@ TRACE_EVENT(btrfs_sync_file, + TP_fast_assign( + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); +- struct dentry *parent = dget_parent(dentry); +- struct inode *parent_inode = d_inode(parent); ++ struct inode *parent_inode = d_inode(dentry->d_parent); + +- dput(parent); + TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); + __entry->ino = btrfs_ino(BTRFS_I(inode)); + __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); +-- +2.53.0 + diff --git a/queue-6.12/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch b/queue-6.12/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch new file mode 100644 index 0000000000..2df74e3aee --- /dev/null +++ b/queue-6.12/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch @@ -0,0 +1,46 @@ +From 74dc7f7347d9edd16b24f99f15b8de383f78d875 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 18:02:15 +0530 +Subject: drm/i915/dp: Fix readback for target_rr in Adaptive Sync SDP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ankit Nautiyal + +[ Upstream commit f87abd0c6604fb6cc31cc86fc7ccc6a576924352 ] + +Correct the bit-shift logic to properly readback the 10 bit target_rr from +DB3 and DB4. + +v2: Align the style with readback for vtotal. (Ville) + +Fixes: 12ea89291603 ("drm/i915/dp: Add Read/Write support for Adaptive Sync SDP") +Cc: Mitul Golani +Cc: Ankit Nautiyal +Signed-off-by: Ankit Nautiyal +Reviewed-by: Ville Syrjälä +Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com +(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 756bb0b1c83be..bb5a2a40b64c6 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -4583,7 +4583,7 @@ int intel_dp_as_sdp_unpack(struct drm_dp_as_sdp *as_sdp, + as_sdp->length = sdp->sdp_header.HB3 & DP_ADAPTIVE_SYNC_SDP_LENGTH; + as_sdp->mode = sdp->db[0] & DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE; + as_sdp->vtotal = (sdp->db[2] << 8) | sdp->db[1]; +- as_sdp->target_rr = (u64)sdp->db[3] | ((u64)sdp->db[4] & 0x3); ++ as_sdp->target_rr = ((sdp->db[4] & 0x3) << 8) | sdp->db[3]; + as_sdp->target_rr_divider = sdp->db[4] & 0x20 ? true : false; + + return 0; +-- +2.53.0 + diff --git a/queue-6.12/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch b/queue-6.12/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch new file mode 100644 index 0000000000..6f21b8f508 --- /dev/null +++ b/queue-6.12/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch @@ -0,0 +1,50 @@ +From c0026c9a730bea621e23a95e48ab7dce23732d09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 20:21:38 +0300 +Subject: drm/msm/dsi: don't dump registers past the mapped region + +From: Dmitry Baryshkov + +[ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] + +On DSI 6G platforms the IO address space is internally adjusted by +io_offset. Later this adjusted address might be used for memory dumping. +However the size that is used for memory dumping isn't adjusted to +account for the io_offset, leading to the potential access to the +unmapped region. Lower ctrl_size by the io_offset value to prevent +access past the mapped area. + + msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) + msm_dsi_host_snapshot+0x4c/0x78 [msm] + msm_dsi_snapshot+0x28/0x50 [msm] + msm_disp_snapshot_capture_state+0x74/0x140 [msm] + msm_disp_snapshot_state_sync+0x60/0x90 [msm] + _msm_disp_snapshot_work+0x30/0x90 [msm] + kthread_worker_fn+0xdc/0x460 + kthread+0x120/0x140 + +Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Patchwork: https://patchwork.freedesktop.org/patch/721747/ +Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c +index 6f538c578f740..c9b580771609b 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_host.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_host.c +@@ -1942,6 +1942,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) + + /* fixup base address by io offset */ + msm_host->ctrl_base += cfg->io_offset; ++ msm_host->ctrl_size -= cfg->io_offset; + + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, +-- +2.53.0 + diff --git a/queue-6.12/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-6.12/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..1c171c529b --- /dev/null +++ b/queue-6.12/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,51 @@ +From b7ba8f82f3365c56b079c40450bd08625144f7e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index 2a94e82316f95..8231488577f4d 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -362,14 +362,15 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, size_t len, int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + /* The arm-smmu driver expects the addresses to be sign extended */ + if (iova & BIT_ULL(48)) + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-6.12/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..378c5eeeb1 --- /dev/null +++ b/queue-6.12/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From a843751ccca02753bfe064712d9f6cb07cc10d54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index 4d55e3cf570f0..a966a03167cc0 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -9,7 +9,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + if (!(*reg)) + *reg = kvzalloc(len_padded, GFP_KERNEL); +@@ -51,8 +51,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -61,6 +61,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -70,6 +71,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -189,7 +201,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-6.12/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch b/queue-6.12/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch new file mode 100644 index 0000000000..29496e92fa --- /dev/null +++ b/queue-6.12/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch @@ -0,0 +1,56 @@ +From f652db0b8bce4fda044a560621dcea94eb6bf21c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:41:34 +0000 +Subject: drm/xe/gsc: Fix double-free of managed BO in error path + +From: Shuicheng Lin + +[ Upstream commit d3ded53fab90996e7d94a39049e11962dd066725 ] + +The error path in xe_gsc_init_post_hwconfig() explicitly frees a BO +allocated with xe_managed_bo_create_pin_map() via +xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm +cleanup action registered, this causes a double-free when devm +unwinds during probe failure. + +Remove the explicit free and let devm handle it, consistent with +all other xe_managed_bo_create_pin_map() callers. + +Fixes: 2e5d47fe7839 ("drm/xe/uc: Use managed bo for HuC and GSC objects") +Reviewed-by: Daniele Ceraolo Spurio +Assisted-by: Claude:claude-opus-4.6 +Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gsc.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c +index efc480d34c9dd..6786773f4560b 100644 +--- a/drivers/gpu/drm/xe/xe_gsc.c ++++ b/drivers/gpu/drm/xe/xe_gsc.c +@@ -485,8 +485,7 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + EXEC_QUEUE_FLAG_PERMANENT, 0); + if (IS_ERR(q)) { + xe_gt_err(gt, "Failed to create queue for GSC submission\n"); +- err = PTR_ERR(q); +- goto out_bo; ++ return PTR_ERR(q); + } + + wq = alloc_ordered_workqueue("gsc-ordered-wq", 0); +@@ -509,8 +508,6 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + + out_q: + xe_exec_queue_put(q); +-out_bo: +- xe_bo_unpin_map_no_vm(bo); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.12/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch b/queue-6.12/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch new file mode 100644 index 0000000000..143db7d645 --- /dev/null +++ b/queue-6.12/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch @@ -0,0 +1,55 @@ +From c24333634ba827f7f5a252604d84e4b3940b164a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 20:32:10 +0000 +Subject: drm/xe/oa: Fix exec_queue leak on width check in stream open + +From: Shuicheng Lin + +[ Upstream commit 4d25342543c01310fc4e0cba7cb17c775e2421e2 ] + +In xe_oa_stream_open_ioctl(), when param.exec_q->width > 1 the +function returns -EOPNOTSUPP directly, skipping the existing +err_exec_q cleanup path. The exec_queue reference obtained by +xe_exec_queue_lookup() is leaked. + +The exec queue holds a reference on the xe_file, which is only +dropped during queue teardown. The leaked lookup ref is not on +the file's exec_queue xarray, so file close cannot release it. +This keeps both the exec queue and the file private state pinned +indefinitely. + +Jump to err_exec_q instead of returning directly so the reference +is released. + +Fixes: f0ed39830e60 ("xe/oa: Fix query mode of operation for OAR/OAC") +Assisted-by: Claude:claude-opus-4.6 +Reviewed-by: Ashutosh Dixit +Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c +index fe997494a6f99..5e38afc46e20e 100644 +--- a/drivers/gpu/drm/xe/xe_oa.c ++++ b/drivers/gpu/drm/xe/xe_oa.c +@@ -2019,8 +2019,10 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f + if (XE_IOCTL_DBG(oa->xe, !param.exec_q)) + return -ENOENT; + +- if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) +- return -EOPNOTSUPP; ++ if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) { ++ ret = -EOPNOTSUPP; ++ goto err_exec_q; ++ } + } + + /* +-- +2.53.0 + diff --git a/queue-6.12/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch b/queue-6.12/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch new file mode 100644 index 0000000000..c870b37d3e --- /dev/null +++ b/queue-6.12/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch @@ -0,0 +1,76 @@ +From a587a6370bd8be5a4c98407845ed822f09e7ff5a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 23:19:18 +0530 +Subject: drm/xe/pf: Fix CFI failure in debugfs access + +From: Mohanram Meenakshisundaram + +[ Upstream commit 96bf49b526e2d03a2b7f6e861925a08f46ed0d28 ] + +Reading debugfs file (/sys/kernel/debug/dri/0/gt*/pf/adverse_events) +with CFI (Control Flow Integrity) enabled, the kernel panics at +xe_gt_debugfs_simple_show+0x82/0xc0. + +xe_gt_debugfs_simple_show() declare a function pointer expecting int +return type, but xe_gt_sriov_pf_monitor_print_events() is void return +type, leading to CFI failure and kernel panic. + +[507620.973657] CFI failure at xe_gt_debugfs_simple_show+0x82/0xc0 [xe] +(target: xe_gt_sriov_pf_monitor_print_events+0x0/0x130 [xe]; expected +type: 0xd72c7139) + +Fix xe_gt_sriov_pf_monitor_print_events() function by updating to return +an int type. + +Fixes: 1c99d3d3edab ("drm/xe/pf: Expose PF monitor details via debugfs") +Signed-off-by: Mohanram Meenakshisundaram +Reviewed-by: Michal Wajdeczko +Signed-off-by: Michal Wajdeczko +Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com +(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c | 6 +++++- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h | 2 +- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +index 7d532bded02a8..a85ba44353789 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +@@ -114,8 +114,10 @@ int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 + * VFs with no events are not printed. + * + * This function can only be called on PF. ++ * ++ * Return: always 0 + */ +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) + { + unsigned int n, total_vfs = xe_gt_sriov_pf_get_totalvfs(gt); + const struct xe_gt_sriov_monitor *data; +@@ -144,4 +146,6 @@ void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p + #undef __format + #undef __value + } ++ ++ return 0; + } +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +index 7ca9351a271b7..0b8f088d3a16a 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +@@ -13,7 +13,7 @@ struct drm_printer; + struct xe_gt; + + void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid); +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); + + #ifdef CONFIG_PCI_IOV + int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len); +-- +2.53.0 + diff --git a/queue-6.12/drm-xe-vf-fix-signature-of-print-functions.patch b/queue-6.12/drm-xe-vf-fix-signature-of-print-functions.patch new file mode 100644 index 0000000000..90a9fd36b3 --- /dev/null +++ b/queue-6.12/drm-xe-vf-fix-signature-of-print-functions.patch @@ -0,0 +1,121 @@ +From 5e02f1675e087146277b8f75e973bd5c73053c4f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 17:57:26 +0200 +Subject: drm/xe/vf: Fix signature of print functions + +From: Michal Wajdeczko + +[ Upstream commit 9bb2f1d7e6e58b8e434ddc2048c661bf87ccdf2a ] + +We have plugged-in existing VF print functions into our GT debugfs +show helper as-is, but we missed that the helper expects functions +to return int, while they were defined as void. This can lead to +errors being reported when CFI is enabled. + +Fixes: 63d8cb8fe3dd ("drm/xe/vf: Expose SR-IOV VF attributes to GT debugfs") +Signed-off-by: Michal Wajdeczko +Cc: Mohanram Meenakshisundaram +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com +(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_vf.c | 24 ++++++++++++++++++------ + drivers/gpu/drm/xe/xe_gt_sriov_vf.h | 6 +++--- + 2 files changed, 21 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +index 29badbd829ab6..3604d324550e0 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +@@ -959,13 +959,15 @@ void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val) + } + + /** +- * xe_gt_sriov_vf_print_config - Print VF self config. ++ * xe_gt_sriov_vf_print_config() - Print VF self config. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config; + struct xe_device *xe = gt_to_xe(gt); +@@ -987,16 +989,20 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + + drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs); + drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF. ++ * xe_gt_sriov_vf_print_runtime() - Print VF's runtime regs received from PF. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + { + struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs; + unsigned int size = gt->sriov.vf.runtime.num_regs; +@@ -1005,16 +1011,20 @@ void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + + for (; size--; vf_regs++) + drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_version - Print VF ABI versions. ++ * xe_gt_sriov_vf_print_version() - Print VF ABI versions. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_gt_sriov_vf_guc_version *guc_version = >->sriov.vf.guc_version; + struct xe_gt_sriov_vf_relay_version *pf_version = >->sriov.vf.pf_version; +@@ -1042,4 +1052,6 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR); + drm_printf(p, "\thandshake:\t%u.%u\n", + pf_version->major, pf_version->minor); ++ ++ return 0; + } +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +index 576ff5e795a8b..cf745ea4ee99f 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +@@ -25,8 +25,8 @@ u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt); + u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg); + void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val); + +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); + + #endif +-- +2.53.0 + diff --git a/queue-6.12/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-6.12/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..2320a4de18 --- /dev/null +++ b/queue-6.12/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From 53d0bfec51ca42b51a42bc9337ab461c2293694d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch b/queue-6.12/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch new file mode 100644 index 0000000000..9305b76943 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch @@ -0,0 +1,52 @@ +From 27cc9472499928ccef77b1969e21a37ac8c68c7e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:33 +0100 +Subject: firmware: arm_ffa: Align RxTx buffer size before mapping + +From: Sudeep Holla + +[ Upstream commit 0399e3f872ca3d78044bb715a73ea645806d2c7b ] + +Commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during +RXTX_MAP") advertises PAGE_ALIGN(rxtx_bufsz) to firmware when mapping the +buffers but the driver continues to stores the minimum FF-A buffer size +in drv_info->rxtx_bufsz which is used elsewhere in the driver. + +Align the size before storing it so that the allocation, validation and +FFA_RXTX_MAP all use the same buffer size. + +Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") +Cc: Sebastian Ene +Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com +Reviewed-by: Sebastian Ene +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 521007bfa35a4..f35c3a56326c7 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1929,6 +1929,7 @@ static int __init ffa_init(void) + rxtx_bufsz = SZ_4K; + } + ++ rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); + drv_info->rxtx_bufsz = rxtx_bufsz; + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { +@@ -1944,7 +1945,7 @@ static int __init ffa_init(void) + + ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), + virt_to_phys(drv_info->rx_buffer), +- PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE); ++ rxtx_bufsz / FFA_PAGE_SIZE); + if (ret) { + pr_err("failed to register FFA RxTx buffers\n"); + goto free_pages; +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-allow-multiple-uuids-per-partition-.patch b/queue-6.12/firmware-arm_ffa-allow-multiple-uuids-per-partition-.patch new file mode 100644 index 0000000000..5b5064be56 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-allow-multiple-uuids-per-partition-.patch @@ -0,0 +1,295 @@ +From 7faa0338e46c75f2962ed6dcd7c1e4faaf6c851f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 15:38:59 +0000 +Subject: firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI + callback + +From: Sudeep Holla + +[ Upstream commit be61da938576671c664382a059f961d7b4b2fc41 ] + +A partition can implement multiple UUIDs and currently we successfully +register each UUID service as a FF-A device. However when adding the +same partition info to the XArray which tracks the SRI callbacks more +than once, it fails. + +In order to allow multiple UUIDs per partition to register SRI callbacks +the partition information stored in the XArray needs to be extended to +a listed list. + +A function to remove the list of partition information in the XArray +is not added as there are no users at the time. All the partitions are +added at probe/initialisation and removed at cleanup stage. + +Tested-by: Viresh Kumar +Message-Id: <20250217-ffa_updates-v3-18-bd1d9de615e7@arm.com> +Signed-off-by: Sudeep Holla +Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0") +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 159 ++++++++++++++++++++++-------- + 1 file changed, 117 insertions(+), 42 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index eaaebb841b782..6961cb44194a1 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -871,27 +871,32 @@ struct ffa_dev_part_info { + ffa_sched_recv_cb callback; + void *cb_data; + rwlock_t rw_lock; ++ struct ffa_device *dev; ++ struct list_head node; + }; + + static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu) + { +- struct ffa_dev_part_info *partition; ++ struct ffa_dev_part_info *partition = NULL, *tmp; + ffa_sched_recv_cb callback; ++ struct list_head *phead; + void *cb_data; + +- partition = xa_load(&drv_info->partition_info, part_id); +- if (!partition) { ++ phead = xa_load(&drv_info->partition_info, part_id); ++ if (!phead) { + pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id); + return; + } + +- read_lock(&partition->rw_lock); +- callback = partition->callback; +- cb_data = partition->cb_data; +- read_unlock(&partition->rw_lock); ++ list_for_each_entry_safe(partition, tmp, phead, node) { ++ read_lock(&partition->rw_lock); ++ callback = partition->callback; ++ cb_data = partition->cb_data; ++ read_unlock(&partition->rw_lock); + +- if (callback) +- callback(vcpu, is_per_vcpu, cb_data); ++ if (callback) ++ callback(vcpu, is_per_vcpu, cb_data); ++ } + } + + /* +@@ -1101,18 +1106,29 @@ struct notifier_cb_info { + enum notify_type type; + }; + +-static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback, +- void *cb_data, bool is_registration) ++static int ++ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, ++ void *cb_data, bool is_registration) + { +- struct ffa_dev_part_info *partition; ++ struct ffa_dev_part_info *partition = NULL, *tmp; ++ struct list_head *phead; + bool cb_valid; + + if (ffa_notifications_disabled()) + return -EOPNOTSUPP; + +- partition = xa_load(&drv_info->partition_info, part_id); ++ phead = xa_load(&drv_info->partition_info, dev->vm_id); ++ if (!phead) { ++ pr_err("%s: Invalid partition ID 0x%x\n", __func__, dev->vm_id); ++ return -EINVAL; ++ } ++ ++ list_for_each_entry_safe(partition, tmp, phead, node) ++ if (partition->dev == dev) ++ break; ++ + if (!partition) { +- pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id); ++ pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id); + return -EINVAL; + } + +@@ -1134,12 +1150,12 @@ static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback, + static int ffa_sched_recv_cb_register(struct ffa_device *dev, + ffa_sched_recv_cb cb, void *cb_data) + { +- return ffa_sched_recv_cb_update(dev->vm_id, cb, cb_data, true); ++ return ffa_sched_recv_cb_update(dev, cb, cb_data, true); + } + + static int ffa_sched_recv_cb_unregister(struct ffa_device *dev) + { +- return ffa_sched_recv_cb_update(dev->vm_id, NULL, NULL, false); ++ return ffa_sched_recv_cb_update(dev, NULL, NULL, false); + } + + static int ffa_notification_bind(u16 dst_id, u64 bitmap, u32 flags) +@@ -1420,37 +1436,101 @@ static struct notifier_block ffa_bus_nb = { + .notifier_call = ffa_bus_notifier, + }; + +-static int ffa_xa_add_partition_info(int vm_id) ++static int ffa_xa_add_partition_info(struct ffa_device *dev) + { + struct ffa_dev_part_info *info; +- int ret; ++ struct list_head *head, *phead; ++ int ret = -ENOMEM; ++ ++ phead = xa_load(&drv_info->partition_info, dev->vm_id); ++ if (phead) { ++ head = phead; ++ list_for_each_entry(info, head, node) { ++ if (info->dev == dev) { ++ pr_err("%s: duplicate dev %p part ID 0x%x\n", ++ __func__, dev, dev->vm_id); ++ return -EEXIST; ++ } ++ } ++ } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) +- return -ENOMEM; ++ return ret; + + rwlock_init(&info->rw_lock); +- ret = xa_insert(&drv_info->partition_info, vm_id, info, GFP_KERNEL); +- if (ret) { +- pr_err("%s: failed to save partition ID 0x%x - ret:%d. Abort.\n", +- __func__, vm_id, ret); +- kfree(info); ++ info->dev = dev; ++ ++ if (!phead) { ++ phead = kzalloc(sizeof(*phead), GFP_KERNEL); ++ if (!phead) ++ goto free_out; ++ ++ INIT_LIST_HEAD(phead); ++ ++ ret = xa_insert(&drv_info->partition_info, dev->vm_id, phead, ++ GFP_KERNEL); ++ if (ret) { ++ pr_err("%s: failed to save part ID 0x%x Ret:%d\n", ++ __func__, dev->vm_id, ret); ++ goto free_out; ++ } + } ++ list_add(&info->node, phead); ++ return 0; ++ ++free_out: ++ kfree(phead); ++ kfree(info); ++ return ret; ++} ++ ++static int ffa_setup_host_partition(int vm_id) ++{ ++ struct ffa_partition_info buf = { 0 }; ++ struct ffa_device *ffa_dev; ++ int ret; ++ ++ buf.id = vm_id; ++ ffa_dev = ffa_device_register(&buf, &ffa_drv_ops); ++ if (!ffa_dev) { ++ pr_err("%s: failed to register host partition ID 0x%x\n", ++ __func__, vm_id); ++ return -EINVAL; ++ } ++ ++ ret = ffa_xa_add_partition_info(ffa_dev); ++ if (ret) ++ return ret; ++ ++ if (ffa_notifications_disabled()) ++ return 0; ++ ++ ret = ffa_sched_recv_cb_update(ffa_dev, ffa_self_notif_handle, ++ drv_info, true); ++ if (ret) ++ pr_info("Failed to register driver sched callback %d\n", ret); + + return ret; + } + + static void ffa_partitions_cleanup(void) + { +- struct ffa_dev_part_info *info; ++ struct list_head *phead; + unsigned long idx; + + /* Clean up/free all registered devices */ + ffa_devices_unregister(); + +- xa_for_each(&drv_info->partition_info, idx, info) { ++ xa_for_each(&drv_info->partition_info, idx, phead) { ++ struct ffa_dev_part_info *info, *tmp; ++ + xa_erase(&drv_info->partition_info, idx); +- kfree(info); ++ list_for_each_entry_safe(info, tmp, phead, node) { ++ list_del(&info->node); ++ kfree(info); ++ } ++ kfree(phead); + } + + xa_destroy(&drv_info->partition_info); +@@ -1493,7 +1573,7 @@ static int ffa_setup_partitions(void) + !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC)) + ffa_mode_32bit_set(ffa_dev); + +- if (ffa_xa_add_partition_info(ffa_dev->vm_id)) { ++ if (ffa_xa_add_partition_info(ffa_dev)) { + ffa_device_unregister(ffa_dev); + continue; + } +@@ -1501,12 +1581,16 @@ static int ffa_setup_partitions(void) + + kfree(pbuf); + +- /* Check if the host is already added as part of partition info */ ++ /* ++ * Check if the host is already added as part of partition info ++ * No multiple UUID possible for the host, so just checking if ++ * there is an entry will suffice ++ */ + if (xa_load(&drv_info->partition_info, drv_info->vm_id)) + return 0; + + /* Allocate for the host */ +- ret = ffa_xa_add_partition_info(drv_info->vm_id); ++ ret = ffa_setup_host_partition(drv_info->vm_id); + if (ret) + ffa_partitions_cleanup(); + +@@ -1815,19 +1899,10 @@ static int __init ffa_init(void) + ffa_notifications_setup(); + + ret = ffa_setup_partitions(); +- if (ret) { +- pr_err("failed to setup partitions\n"); +- goto cleanup_notifs; +- } +- +- ret = ffa_sched_recv_cb_update(drv_info->vm_id, ffa_self_notif_handle, +- drv_info, true); +- if (ret) +- pr_info("Failed to register driver sched callback %d\n", ret); +- +- return 0; ++ if (!ret) ++ return ret; + +-cleanup_notifs: ++ pr_err("failed to setup partitions\n"); + ffa_notifications_cleanup(); + ffa_rxtx_unmap(); + free_pages: +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch b/queue-6.12/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch new file mode 100644 index 0000000000..a54448b948 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch @@ -0,0 +1,100 @@ +From ce710f6b9da6cf26d441fbbed4bdb5ac87b1b341 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:30 +0100 +Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies + +From: Sudeep Holla + +[ Upstream commit 3974ea1938406f9bfa7c1f48d4e43533f447bb08 ] + +The register-based PARTITION_INFO_GET path trusted the firmware-provided +indices when copying partition descriptors into the caller buffer. +Reject inconsistent counts or index progressions so the copy loop cannot +write past the allocated array. + +Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-6-8595ae450034@kernel.org +(fixed cur_idx when exactly one descriptor in the first fragment) +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++------ + 1 file changed, 23 insertions(+), 6 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index f033bd8ee816d..521007bfa35a4 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -307,6 +307,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + #define PART_INFO_ID_MASK GENMASK(15, 0) + #define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) + #define PART_INFO_PROPS_MASK GENMASK(63, 32) ++#define FFA_PART_INFO_GET_REGS_FIRST_REG 3 ++#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 ++#define FFA_PART_INFO_GET_REGS_MAX_DESC \ ++ (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ ++ FFA_PART_INFO_GET_REGS_FIRST_REG) / \ ++ FFA_PART_INFO_GET_REGS_REGS_PER_DESC) + #define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) + #define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) + #define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) +@@ -314,15 +320,13 @@ static int + __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + struct ffa_partition_info *buffer, int num_parts) + { +- u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; ++ u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0; + struct ffa_partition_info *buf = buffer; + ffa_value_t partition_info; + + do { + __le64 *regs; +- int idx; +- +- start_idx = prev_idx ? prev_idx + 1 : 0; ++ int idx, nr_desc, buf_idx; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_PARTITION_INFO_GET_REGS, +@@ -338,15 +342,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + count = PARTITION_COUNT(partition_info.a2); + if (!buffer || !num_parts) /* count only */ + return count; ++ if (count > num_parts) ++ return -EINVAL; + + cur_idx = CURRENT_INDEX(partition_info.a2); ++ if (cur_idx < start_idx || cur_idx >= count) ++ return -EINVAL; ++ ++ nr_desc = cur_idx - start_idx + 1; ++ if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) ++ return -EINVAL; ++ ++ buf_idx = buf - buffer; ++ if (buf_idx + nr_desc > num_parts) ++ return -EINVAL; ++ + tag = UUID_INFO_TAG(partition_info.a2); + buf_sz = PARTITION_INFO_SZ(partition_info.a2); + if (buf_sz > sizeof(*buffer)) + buf_sz = sizeof(*buffer); + + regs = (void *)&partition_info.a3; +- for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) { ++ for (idx = 0; idx < nr_desc; idx++, buf++) { + union { + uuid_t uuid; + u64 regs[2]; +@@ -364,7 +381,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + uuid_copy(&buf->uuid, &uuid_regs.uuid); + regs += 3; + } +- prev_idx = cur_idx; ++ start_idx = cur_idx + 1; + + } while (cur_idx < (count - 1)); + +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-6.12/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..e98dae90c6 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From 8129bf2937a99dfdb77a68173b144c0cb6fdfaa4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index dea3eb741d95d..ef41815c0109e 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-fix-big-endian-support-in-__ffa_par.patch b/queue-6.12/firmware-arm_ffa-fix-big-endian-support-in-__ffa_par.patch new file mode 100644 index 0000000000..789d3266c2 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-fix-big-endian-support-in-__ffa_par.patch @@ -0,0 +1,90 @@ +From b2e6055b1976439f447ebf68c1f41cfd2aa89145 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 15:38:46 +0000 +Subject: firmware: arm_ffa: Fix big-endian support in + __ffa_partition_info_regs_get() + +From: Sudeep Holla + +[ Upstream commit 7bc0f589c81d62bc95f9ed142847219fc5d8ef6c ] + +Currently the FF-A driver doesn't support big-endian correctly. It is +hard to regularly test the setup due to lack of test infrastructure and +tools. + +In order to support full stack, we need to take small steps in getting +the support for big-endian kernel added slowly. This change fixes the +support in __ffa_partition_info_regs_get() so that the response from the +firmware are converted correctly as required. With this change, we can +enumerate all the FF-A devices correctly in the big-endian kernel if the +FFA_PARTITION_INFO_REGS_GET is supported. + +Tested-by: Viresh Kumar +Message-Id: <20250217-ffa_updates-v3-5-bd1d9de615e7@arm.com> +Signed-off-by: Sudeep Holla +Stable-dep-of: 3974ea193840 ("firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies") +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index a6c5f89476c06..f033bd8ee816d 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -304,14 +304,24 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + #define CURRENT_INDEX(x) ((u16)(FIELD_GET(CURRENT_INDEX_MASK, (x)))) + #define UUID_INFO_TAG(x) ((u16)(FIELD_GET(UUID_INFO_TAG_MASK, (x)))) + #define PARTITION_INFO_SZ(x) ((u16)(FIELD_GET(PARTITION_INFO_SZ_MASK, (x)))) ++#define PART_INFO_ID_MASK GENMASK(15, 0) ++#define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) ++#define PART_INFO_PROPS_MASK GENMASK(63, 32) ++#define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) ++#define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) ++#define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) + static int + __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + struct ffa_partition_info *buffer, int num_parts) + { + u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; ++ struct ffa_partition_info *buf = buffer; + ffa_value_t partition_info; + + do { ++ __le64 *regs; ++ int idx; ++ + start_idx = prev_idx ? prev_idx + 1 : 0; + + invoke_ffa_fn((ffa_value_t){ +@@ -335,8 +345,25 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + if (buf_sz > sizeof(*buffer)) + buf_sz = sizeof(*buffer); + +- memcpy(buffer + prev_idx * buf_sz, &partition_info.a3, +- (cur_idx - start_idx + 1) * buf_sz); ++ regs = (void *)&partition_info.a3; ++ for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) { ++ union { ++ uuid_t uuid; ++ u64 regs[2]; ++ } uuid_regs = { ++ .regs = { ++ le64_to_cpu(*(regs + 1)), ++ le64_to_cpu(*(regs + 2)), ++ } ++ }; ++ u64 val = *(u64 *)regs; ++ ++ buf->id = PART_INFO_ID(val); ++ buf->exec_ctxt = PART_INFO_EXEC_CXT(val); ++ buf->properties = PART_INFO_PROPERTIES(val); ++ uuid_copy(&buf->uuid, &uuid_regs.uuid); ++ regs += 3; ++ } + prev_idx = cur_idx; + + } while (cur_idx < (count - 1)); +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch b/queue-6.12/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch new file mode 100644 index 0000000000..64632e39ac --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch @@ -0,0 +1,45 @@ +From 632371eb23fae1c6de641d1f05932921cbd31a71 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:28 +0100 +Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in + workqueue + +From: Sudeep Holla + +[ Upstream commit 9985d5357ed93af0d1933969c247e966957730e1 ] + +Per-vcpu notification handling already runs from a per-cpu work item on +the target cpu. Routing that path back through smp_call_function_single() +re-enters the call-function IPI path and executes the notification +handler with interrupts disabled. That makes the framework path unsafe, +since it takes a mutex, allocates memory with GFP_KERNEL, and invokes +client callbacks. + +Handle per-vcpu self notifications directly from the existing per-cpu +work item instead. This keeps the per-vcpu path in task context and +avoids the extra IPI hop entirely. + +Fixes: 3a3e2b83e805 ("firmware: arm_ffa: Avoid queuing work when running on the worker queue") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-4-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 61d4a4b7d97b6..39f19acdce904 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1336,7 +1336,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work) + struct ffa_drv_info *info = container_of(work, struct ffa_drv_info, + notif_pcpu_work); + +- ffa_self_notif_handle(smp_processor_id(), true, info); ++ notif_get_and_handle(info); + } + + static const struct ffa_info_ops ffa_drv_info_ops = { +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch b/queue-6.12/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch new file mode 100644 index 0000000000..f13a8d6085 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch @@ -0,0 +1,53 @@ +From 9dc0e1e130a36bd3aff57c9c08ec479f50cddae2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:35 +0100 +Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup + +From: Sudeep Holla + +[ Upstream commit a6848a50404eefb6f0b131c21881a2d8d21b31a9 ] + +ffa_sched_recv_cb_update() used list_for_each_entry_safe() to search for +a matching partition and then tested the iterator against NULL. That is +not a valid end-of-list check for circular lists and can fall through +with an invalid pointer. Use a normal iterator and detect the not-found +case correctly before touching the partition state. + +Fixes: be61da938576 ("firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI callback") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-11-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index f35c3a56326c7..89474fa3c80e5 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1155,7 +1155,7 @@ static int + ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + void *cb_data, bool is_registration) + { +- struct ffa_dev_part_info *partition = NULL, *tmp; ++ struct ffa_dev_part_info *partition = NULL; + struct list_head *phead; + bool cb_valid; + +@@ -1168,11 +1168,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + return -EINVAL; + } + +- list_for_each_entry_safe(partition, tmp, phead, node) ++ list_for_each_entry(partition, phead, node) + if (partition->dev == dev) + break; + +- if (!partition) { ++ if (&partition->node == phead) { + pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-refactor-addition-of-partition-info.patch b/queue-6.12/firmware-arm_ffa-refactor-addition-of-partition-info.patch new file mode 100644 index 0000000000..0eb43a20b4 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-refactor-addition-of-partition-info.patch @@ -0,0 +1,105 @@ +From ef63f66b79720f7ec18c222322bf8757d2d5ed86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 15:38:47 +0000 +Subject: firmware: arm_ffa: Refactor addition of partition information into + XArray + +From: Viresh Kumar + +[ Upstream commit 3c3d6767466ea316869c9f2bdd976aec8ce44545 ] + +Move the common code handling addition of the FF-A partition information +into the XArray as a new routine. No functional change. + +Signed-off-by: Viresh Kumar +Message-Id: <20250217-ffa_updates-v3-6-bd1d9de615e7@arm.com> +Signed-off-by: Sudeep Holla +Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0") +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 47 +++++++++++++++---------------- + 1 file changed, 22 insertions(+), 25 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 39f19acdce904..84c4fe40d5279 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1421,11 +1421,30 @@ static struct notifier_block ffa_bus_nb = { + .notifier_call = ffa_bus_notifier, + }; + ++static int ffa_xa_add_partition_info(int vm_id) ++{ ++ struct ffa_dev_part_info *info; ++ int ret; ++ ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ rwlock_init(&info->rw_lock); ++ ret = xa_insert(&drv_info->partition_info, vm_id, info, GFP_KERNEL); ++ if (ret) { ++ pr_err("%s: failed to save partition ID 0x%x - ret:%d. Abort.\n", ++ __func__, vm_id, ret); ++ kfree(info); ++ } ++ ++ return ret; ++} ++ + static int ffa_setup_partitions(void) + { + int count, idx, ret; + struct ffa_device *ffa_dev; +- struct ffa_dev_part_info *info; + struct ffa_partition_info *pbuf, *tpbuf; + + if (drv_info->version == FFA_VERSION_1_0) { +@@ -1459,20 +1478,10 @@ static int ffa_setup_partitions(void) + !(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC)) + ffa_mode_32bit_set(ffa_dev); + +- info = kzalloc(sizeof(*info), GFP_KERNEL); +- if (!info) { ++ if (ffa_xa_add_partition_info(ffa_dev->vm_id)) { + ffa_device_unregister(ffa_dev); + continue; + } +- rwlock_init(&info->rw_lock); +- ret = xa_insert(&drv_info->partition_info, tpbuf->id, +- info, GFP_KERNEL); +- if (ret) { +- pr_err("%s: failed to save partition ID 0x%x - ret:%d\n", +- __func__, tpbuf->id, ret); +- ffa_device_unregister(ffa_dev); +- kfree(info); +- } + } + + kfree(pbuf); +@@ -1482,20 +1491,8 @@ static int ffa_setup_partitions(void) + return 0; + + /* Allocate for the host */ +- info = kzalloc(sizeof(*info), GFP_KERNEL); +- if (!info) { +- /* Already registered devices are freed on bus_exit */ +- ffa_partitions_cleanup(); +- return -ENOMEM; +- } +- +- rwlock_init(&info->rw_lock); +- ret = xa_insert(&drv_info->partition_info, drv_info->vm_id, +- info, GFP_KERNEL); ++ ret = ffa_xa_add_partition_info(drv_info->vm_id); + if (ret) { +- pr_err("%s: failed to save Host partition ID 0x%x - ret:%d. Abort.\n", +- __func__, drv_info->vm_id, ret); +- kfree(info); + /* Already registered devices are freed on bus_exit */ + ffa_partitions_cleanup(); + } +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-remove-unnecessary-declaration-of-f.patch b/queue-6.12/firmware-arm_ffa-remove-unnecessary-declaration-of-f.patch new file mode 100644 index 0000000000..086dd1dac5 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-remove-unnecessary-declaration-of-f.patch @@ -0,0 +1,85 @@ +From c0b625af957bdd248f275b1c2112087b31d3e01e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 15:38:54 +0000 +Subject: firmware: arm_ffa: Remove unnecessary declaration of + ffa_partitions_cleanup() + +From: Sudeep Holla + +[ Upstream commit 9982cabf403fbd06a120a2d5b21830effd32b370 ] + +In order to keep the uniformity, just move the ffa_partitions_cleanup() +before it's first usage and drop the unnecessary forward declaration. + +No functional change. + +Tested-by: Viresh Kumar +Message-Id: <20250217-ffa_updates-v3-13-bd1d9de615e7@arm.com> +Signed-off-by: Sudeep Holla +Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0") +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 33 +++++++++++++++---------------- + 1 file changed, 16 insertions(+), 17 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 63030a3849a87..eaaebb841b782 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -114,7 +114,6 @@ struct ffa_drv_info { + }; + + static struct ffa_drv_info *drv_info; +-static void ffa_partitions_cleanup(void); + + /* + * The driver must be able to support all the versions from the earliest +@@ -1441,6 +1440,22 @@ static int ffa_xa_add_partition_info(int vm_id) + return ret; + } + ++static void ffa_partitions_cleanup(void) ++{ ++ struct ffa_dev_part_info *info; ++ unsigned long idx; ++ ++ /* Clean up/free all registered devices */ ++ ffa_devices_unregister(); ++ ++ xa_for_each(&drv_info->partition_info, idx, info) { ++ xa_erase(&drv_info->partition_info, idx); ++ kfree(info); ++ } ++ ++ xa_destroy(&drv_info->partition_info); ++} ++ + static int ffa_setup_partitions(void) + { + int count, idx, ret; +@@ -1498,22 +1513,6 @@ static int ffa_setup_partitions(void) + return ret; + } + +-static void ffa_partitions_cleanup(void) +-{ +- struct ffa_dev_part_info *info; +- unsigned long idx; +- +- /* Clean up/free all registered devices */ +- ffa_devices_unregister(); +- +- xa_for_each(&drv_info->partition_info, idx, info) { +- xa_erase(&drv_info->partition_info, idx); +- kfree(info); +- } +- +- xa_destroy(&drv_info->partition_info); +-} +- + /* FFA FEATURE IDs */ + #define FFA_FEAT_NOTIFICATION_PENDING_INT (1) + #define FFA_FEAT_SCHEDULE_RECEIVER_INT (2) +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-6.12/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..73bb90f554 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From 72aff53869b2ef053f359028876fc80de460665f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 15e71a53956e2..61d4a4b7d97b6 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1793,7 +1793,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch b/queue-6.12/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch new file mode 100644 index 0000000000..45ece9b410 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch @@ -0,0 +1,77 @@ +From cc89da6aaae318eafe4a8df907197c63a2acde9b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:29 +0100 +Subject: firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0 + +From: Sudeep Holla + +[ Upstream commit 6d3daa9b8d313f42d52e75590310f26a29b61b44 ] + +For FF-A v1.0 the driver registers a bus notifier to backfill UUID +matching, but the notifier was never unregistered on cleanup paths. +Track the registration state and unregister it during teardown and early +partition-setup failure. + +Fixes: 9dd15934f60d ("firmware: arm_ffa: Move the FF-A v1.0 NULL UUID workaround to bus notifier") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-5-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 6961cb44194a1..a6c5f89476c06 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -100,6 +100,7 @@ struct ffa_drv_info { + bool mem_ops_native; + bool msg_direct_req2_supp; + bool bitmap_created; ++ bool bus_notifier_registered; + bool notif_enabled; + unsigned int sched_recv_irq; + unsigned int notif_pend_irq; +@@ -1436,6 +1437,15 @@ static struct notifier_block ffa_bus_nb = { + .notifier_call = ffa_bus_notifier, + }; + ++static void ffa_bus_notifier_unregister(void) ++{ ++ if (!drv_info->bus_notifier_registered) ++ return; ++ ++ bus_unregister_notifier(&ffa_bus_type, &ffa_bus_nb); ++ drv_info->bus_notifier_registered = false; ++} ++ + static int ffa_xa_add_partition_info(struct ffa_device *dev) + { + struct ffa_dev_part_info *info; +@@ -1519,6 +1529,8 @@ static void ffa_partitions_cleanup(void) + struct list_head *phead; + unsigned long idx; + ++ ffa_bus_notifier_unregister(); ++ + /* Clean up/free all registered devices */ + ffa_devices_unregister(); + +@@ -1546,11 +1558,14 @@ static int ffa_setup_partitions(void) + ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb); + if (ret) + pr_err("Failed to register FF-A bus notifiers\n"); ++ else ++ drv_info->bus_notifier_registered = true; + } + + count = ffa_partition_probe(&uuid_null, &pbuf); + if (count <= 0) { + pr_info("%s: No partitions found, error %d\n", __func__, count); ++ ffa_bus_notifier_unregister(); + return -EINVAL; + } + +-- +2.53.0 + diff --git a/queue-6.12/firmware-arm_ffa-unregister-the-ff-a-devices-when-cl.patch b/queue-6.12/firmware-arm_ffa-unregister-the-ff-a-devices-when-cl.patch new file mode 100644 index 0000000000..754e65d162 --- /dev/null +++ b/queue-6.12/firmware-arm_ffa-unregister-the-ff-a-devices-when-cl.patch @@ -0,0 +1,101 @@ +From 0c9938f364c69defec5d911301afc13f710f437a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Feb 2025 15:38:49 +0000 +Subject: firmware: arm_ffa: Unregister the FF-A devices when cleaning up the + partitions + +From: Sudeep Holla + +[ Upstream commit 46dcd68aaccac0812c12ec3f4e59c8963e2760ad ] + +Both the FF-A core and the bus were in a single module before the +commit 18c250bd7ed0 ("firmware: arm_ffa: Split bus and driver into distinct modules"). + +The arm_ffa_bus_exit() takes care of unregistering all the FF-A devices. +Now that there are 2 distinct modules, if the core driver is unloaded and +reloaded, it will end up adding duplicate FF-A devices as the previously +registered devices weren't unregistered when we cleaned up the modules. + +Fix the same by unregistering all the FF-A devices on the FF-A bus during +the cleaning up of the partitions and hence the cleanup of the module. + +Fixes: 18c250bd7ed0 ("firmware: arm_ffa: Split bus and driver into distinct modules") +Tested-by: Viresh Kumar +Message-Id: <20250217-ffa_updates-v3-8-bd1d9de615e7@arm.com> +Signed-off-by: Sudeep Holla +Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0") +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 3 ++- + drivers/firmware/arm_ffa/driver.c | 7 ++++--- + include/linux/arm_ffa.h | 3 +++ + 3 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index ef41815c0109e..50bbc18599f74 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -162,11 +162,12 @@ static int __ffa_devices_unregister(struct device *dev, void *data) + return 0; + } + +-static void ffa_devices_unregister(void) ++void ffa_devices_unregister(void) + { + bus_for_each_dev(&ffa_bus_type, NULL, NULL, + __ffa_devices_unregister); + } ++EXPORT_SYMBOL_GPL(ffa_devices_unregister); + + bool ffa_device_is_valid(struct ffa_device *ffa_dev) + { +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 84c4fe40d5279..63030a3849a87 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1492,10 +1492,8 @@ static int ffa_setup_partitions(void) + + /* Allocate for the host */ + ret = ffa_xa_add_partition_info(drv_info->vm_id); +- if (ret) { +- /* Already registered devices are freed on bus_exit */ ++ if (ret) + ffa_partitions_cleanup(); +- } + + return ret; + } +@@ -1505,6 +1503,9 @@ static void ffa_partitions_cleanup(void) + struct ffa_dev_part_info *info; + unsigned long idx; + ++ /* Clean up/free all registered devices */ ++ ffa_devices_unregister(); ++ + xa_for_each(&drv_info->partition_info, idx, info) { + xa_erase(&drv_info->partition_info, idx); + kfree(info); +diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h +index 74169dd0f6594..53f2837ce7df4 100644 +--- a/include/linux/arm_ffa.h ++++ b/include/linux/arm_ffa.h +@@ -176,6 +176,7 @@ void ffa_device_unregister(struct ffa_device *ffa_dev); + int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + const char *mod_name); + void ffa_driver_unregister(struct ffa_driver *driver); ++void ffa_devices_unregister(void); + bool ffa_device_is_valid(struct ffa_device *ffa_dev); + + #else +@@ -188,6 +189,8 @@ ffa_device_register(const struct ffa_partition_info *part_info, + + static inline void ffa_device_unregister(struct ffa_device *dev) {} + ++static inline void ffa_devices_unregister(void) {} ++ + static inline int + ffa_driver_register(struct ffa_driver *driver, struct module *owner, + const char *mod_name) +-- +2.53.0 + diff --git a/queue-6.12/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-6.12/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..029ae02aaf --- /dev/null +++ b/queue-6.12/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From 7de52e482c89ce8e4f5ef7d4fe76da3401aaf4b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index d44a3e4c2a09c..e77ec4b205f42 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1324,6 +1324,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1331,9 +1332,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-6.12/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch b/queue-6.12/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch new file mode 100644 index 0000000000..2004e99863 --- /dev/null +++ b/queue-6.12/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch @@ -0,0 +1,70 @@ +From 32349dd4cb1d190154917545ca25443b303e2323 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Nov 2024 22:16:15 +0200 +Subject: gpiolib: cdev: use !mem_is_zero() instead of memchr_inv(s, 0, n) + +From: Andy Shevchenko + +[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ] + +Use the mem_is_zero() helper where possible. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index bd2921ef29a14..d44a3e4c2a09c 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -26,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1331,7 +1331,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + +- if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) ++ if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + + for (i = 0; i < num_lines; i++) { +@@ -1746,7 +1746,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) + return -EINVAL; + +- if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) ++ if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) + return -EINVAL; + + lc = &ulr.config; +@@ -2516,7 +2516,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +- if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) ++ if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) + return -EINVAL; + + desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset); +-- +2.53.0 + diff --git a/queue-6.12/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-6.12/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..b74fae53ae --- /dev/null +++ b/queue-6.12/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 11150f2c88dc12bb2262ec49b0635dd04bf7bdb0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index d9e33dde89899..9d396d2e534d0 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -234,7 +234,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-6.12/hid-uclogic-fix-regression-of-input-name-assignment.patch b/queue-6.12/hid-uclogic-fix-regression-of-input-name-assignment.patch new file mode 100644 index 0000000000..379f6d3154 --- /dev/null +++ b/queue-6.12/hid-uclogic-fix-regression-of-input-name-assignment.patch @@ -0,0 +1,44 @@ +From 7625271ccb55aa33a49a28834f74920809ce7b9f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 10:33:16 +0200 +Subject: HID: uclogic: Fix regression of input name assignment + +From: Takashi Iwai + +[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] + +The previous fix for adding the devm_kasprintf() return check in the +commit bd07f751208b ("HID: uclogic: Add NULL check in +uclogic_input_configured()") changed the condition of hi->input->name +assignment, and it resulted in missing the proper input device name +when no custom suffix is defined. + +Restore the conditional to the original content to address the +regression. + +Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") +Signed-off-by: Takashi Iwai +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-uclogic-core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c +index 321c43fb06ae0..f8708a1ec7cc8 100644 +--- a/drivers/hid/hid-uclogic-core.c ++++ b/drivers/hid/hid-uclogic-core.c +@@ -142,7 +142,9 @@ static int uclogic_input_configured(struct hid_device *hdev, + suffix = "System Control"; + break; + } +- } else { ++ } ++ ++ if (suffix) { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) +-- +2.53.0 + diff --git a/queue-6.12/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-6.12/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..76390b1426 --- /dev/null +++ b/queue-6.12/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From e4b72fa4cc7eee84ec823e5750699207a02481b1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index a7c5108328240..d185b1aba7a47 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + int ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-6.12/ice-fix-setting-rss-vsi-hash-for-e830.patch b/queue-6.12/ice-fix-setting-rss-vsi-hash-for-e830.patch new file mode 100644 index 0000000000..abf54752f3 --- /dev/null +++ b/queue-6.12/ice-fix-setting-rss-vsi-hash-for-e830.patch @@ -0,0 +1,56 @@ +From 9779bd7c83c83ecd298ddf0fa446ec2ca031a061 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:14 -0700 +Subject: ice: fix setting RSS VSI hash for E830 + +From: Marcin Szycik + +[ Upstream commit b3cda96feb60d91fe88d52b974ff110dcfa91239 ] + +ice_set_rss_hfunc() performs a VSI update, in which it sets hashing +function, leaving other VSI options unchanged. However, ::q_opt_flags is +mistakenly set to the value of another field, instead of its original +value, probably due to a typo. What happens next is hardware-dependent: + +On E810, only the first bit is meaningful (see +ICE_AQ_VSI_Q_OPT_PE_FLTR_EN) and can potentially end up in a different +state than before VSI update. + +On E830, some of the remaining bits are not reserved. Setting them +to some unrelated values can cause the firmware to reject the update +because of invalid settings, or worse - succeed. + +Reproducer: + sudo ethtool -X $PF1 equal 8 + +Output in dmesg: + Failed to configure RSS hash for VSI 6, error -5 + +Fixes: 352e9bf23813 ("ice: enable symmetric-xor RSS for Toeplitz hash function") +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Signed-off-by: Marcin Szycik +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index 2a629b9a9e03a..664bedfbd8054 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -8108,7 +8108,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) + ctx->info.q_opt_rss |= + FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); + ctx->info.q_opt_tc = vsi->info.q_opt_tc; +- ctx->info.q_opt_flags = vsi->info.q_opt_rss; ++ ctx->info.q_opt_flags = vsi->info.q_opt_flags; + + err = ice_update_vsi(hw, vsi->idx, ctx, NULL); + if (err) { +-- +2.53.0 + diff --git a/queue-6.12/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch b/queue-6.12/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch new file mode 100644 index 0000000000..63bc07f22a --- /dev/null +++ b/queue-6.12/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch @@ -0,0 +1,79 @@ +From a402b1074f66f824d5991449d1271f6b0d944fc6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:11 -0700 +Subject: ice: ptp: serialize E825 PHY timer start with PTP lock + +From: Grzegorz Nitka + +[ Upstream commit 781ff8f2d575a794a2a4f11605288ae06757f5eb ] + +ice_start_phy_timer_eth56g() programs TIMETUS registers and issues +INIT_INCVAL without holding the global PTP semaphore. + +This allows concurrent PTP command paths to interleave with PHY timer +start, which can make the sequence fail and leave timer initialization +inconsistent. + +Take the PTP lock around TIMETUS registers programming and INIT_INCVAL +command execution, and make sure the lock is released on all error paths. + +Keep the subsequent sync step outside of this critical section, since +ice_sync_phy_timer_eth56g() takes the same semaphore internally. + +Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products") +Reviewed-by: Arkadiusz Kubalewski +Signed-off-by: Grzegorz Nitka +Reviewed-by: Aleksandr Loktionov +Tested-by: Alexander Nowlin +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +index 478ee1c540142..9bf34fc28533e 100644 +--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c ++++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +@@ -2597,16 +2597,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + hi = rd32(hw, GLTSYN_INCVAL_H(tmr_idx)); + incval = (u64)hi << 32 | lo; + ++ if (!ice_ptp_lock(hw)) { ++ dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n"); ++ return -EBUSY; ++ } ++ + err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval); + if (err) +- return err; ++ goto err_ptp_unlock; + + err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL); + if (err) +- return err; ++ goto err_ptp_unlock; + + ice_ptp_exec_tmr_cmd(hw); + ++ ice_ptp_unlock(hw); ++ + err = ice_sync_phy_timer_eth56g(hw, port); + if (err) + return err; +@@ -2622,6 +2629,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port); + + return 0; ++ ++err_ptp_unlock: ++ ice_ptp_unlock(hw); ++ return err; + } + + /** +-- +2.53.0 + diff --git a/queue-6.12/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch b/queue-6.12/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch new file mode 100644 index 0000000000..3a0a84ae0e --- /dev/null +++ b/queue-6.12/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch @@ -0,0 +1,89 @@ +From 54a5069e387744d72708eec5f90a746f2c69f877 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 10:19:09 -0600 +Subject: io_uring/net: punt IORING_OP_BIND async if it needs file create + +From: Jens Axboe + +[ Upstream commit ccd25890f73c082fe2657ed227b497d6ac5fdc40 ] + +For two reasons: + +1) An opcode cannot block inside io_uring_enter() doing submissions, as + it'll stall the submission side pipeline. + +2) Ending up in sb_start_write() -> __sb_start_write() -> + percpu_down_read_freezable() introduces a new lockdep edge, which it + correctly complains about. + +Check if the socket type is AF_UNIX and has a non-empty pathname. If it +does, mark it REQ_F_FORCE_ASYNC to punt the submission to io-wq rather +than attempt to do it inline. + +Fixes: 7481fd93fa0a ("io_uring: Introduce IORING_OP_BIND") +Reviewed-by: Gabriel Krisman Bertazi +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/net.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/io_uring/net.c b/io_uring/net.c +index 0b730c3a7de46..94b6a15245afb 100644 +--- a/io_uring/net.c ++++ b/io_uring/net.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1785,11 +1786,29 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) + return IOU_OK; + } + ++/* ++ * Check if bind request would potentially end up with filename_create(), ++ * which in turn end up in mnt_want_write() which will grab the fs ++ * percpu start write sem. This can trigger a lockdep warning. ++ */ ++static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len) ++{ ++ const struct sockaddr_un *sun; ++ ++ if (io->addr.ss_family != AF_UNIX) ++ return 0; ++ if (addr_len <= offsetof(struct sockaddr_un, sun_path)) ++ return 0; ++ sun = (const struct sockaddr_un *) &io->addr; ++ return sun->sun_path[0] != '\0'; ++} ++ + int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + { + struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); + struct sockaddr __user *uaddr; + struct io_async_msghdr *io; ++ int ret; + + if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) + return -EINVAL; +@@ -1800,7 +1819,12 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + io = io_msg_alloc_async(req); + if (unlikely(!io)) + return -ENOMEM; +- return move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ if (unlikely(ret)) ++ return ret; ++ if (io_bind_file_create(io, bind->addr_len)) ++ req->flags |= REQ_F_FORCE_ASYNC; ++ return 0; + } + + int io_bind(struct io_kiocb *req, unsigned int issue_flags) +-- +2.53.0 + diff --git a/queue-6.12/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch b/queue-6.12/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch new file mode 100644 index 0000000000..08391b6c73 --- /dev/null +++ b/queue-6.12/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch @@ -0,0 +1,57 @@ +From 5dd9d37dbf8db894f0ce91372705fb4c8b93ba96 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 23:03:28 -0400 +Subject: ipv6: route: Unregister netdevice notifier on BPF init failure + +From: Yuho Choi + +[ Upstream commit 1341db322417266fb5845df81d28305b83a37324 ] + +ip6_route_init() registers ip6_route_dev_notifier before registering the +IPv6 route BPF iterator target. If bpf_iter_register() fails after the +notifier has been registered, the error path currently jumps to +out_register_late_subsys and unwinds the RTNL handlers and pernet route +state without removing the notifier from the netdevice notifier chain. + +This leaves ip6_route_dev_notify() callable after the IPv6 route state it +uses has been torn down. Add a separate unwind label for the BPF iterator +failure path and unregister the netdevice notifier before continuing with +the existing cleanup. + +Fixes: 138d0be35b14 ("net: bpf: Add netlink and ipv6_route bpf_iter targets") +Signed-off-by: Yuho Choi +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 31c9e3b73f2da..0c2303d7e6f89 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6826,7 +6826,7 @@ int __init ip6_route_init(void) + #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) + ret = bpf_iter_register(); + if (ret) +- goto out_register_late_subsys; ++ goto out_register_notifier; + #endif + #endif + +@@ -6840,6 +6840,10 @@ int __init ip6_route_init(void) + out: + return ret; + ++#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) ++out_register_notifier: ++ unregister_netdevice_notifier(&ip6_route_dev_notifier); ++#endif + out_register_late_subsys: + rtnl_unregister_all(PF_INET6); + unregister_pernet_subsys(&ip6_route_net_late_ops); +-- +2.53.0 + diff --git a/queue-6.12/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch b/queue-6.12/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch new file mode 100644 index 0000000000..5474c42250 --- /dev/null +++ b/queue-6.12/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch @@ -0,0 +1,68 @@ +From bf8bf6a1a4cdfed43e48132ee6c23ad804b31a4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Mar 2026 15:32:29 +0800 +Subject: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jiayuan Chen + +[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] + +On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via +run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. + +After irq_work_single() clears BUSY via atomic_cmpxchg(), it still +dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). + +An irq_work_sync() caller on another CPU that enters after BUSY is cleared +can observe BUSY==0 immediately, return, and free the work before those +accesses complete — causing a use-after-free. + +Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire +irq_work_single() execution is within an RCU read-side critical +section. Then add synchronize_rcu() in irq_work_sync() after +rcuwait_wait_event() to ensure the caller waits for the RCU grace period +before returning, preventing premature frees. + +Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") +Suggested-by: Sebastian Andrzej Siewior +Suggested-by: Steven Rostedt +Signed-off-by: Jiayuan Chen +Signed-off-by: Thomas Gleixner +Reviewed-by: Sebastian Andrzej Siewior +Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev +Signed-off-by: Sasha Levin +--- + kernel/irq_work.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/kernel/irq_work.c b/kernel/irq_work.c +index 2f4fb336dda17..188721af8eb31 100644 +--- a/kernel/irq_work.c ++++ b/kernel/irq_work.c +@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) + !arch_irq_work_has_interrupt()) { + rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), + TASK_UNINTERRUPTIBLE); ++ /* ++ * Ensure irq_work_single() does not access @work ++ * after removing IRQ_WORK_BUSY. It is always ++ * accessed within a RCU-read section. ++ */ ++ synchronize_rcu(); + return; + } + +@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); + + static void run_irq_workd(unsigned int cpu) + { ++ guard(rcu)(); + irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + +-- +2.53.0 + diff --git a/queue-6.12/irqchip-ath79-cpu-remove-unused-function.patch b/queue-6.12/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..10262fd764 --- /dev/null +++ b/queue-6.12/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From 1c53bc3dbaac3b9ee8590c617a274d0b6316863c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-6.12/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch b/queue-6.12/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch new file mode 100644 index 0000000000..b24d39b0af --- /dev/null +++ b/queue-6.12/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch @@ -0,0 +1,51 @@ +From d643a53a76bd0a8050585d5b9c2ba1f385e53875 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 23:58:45 +0200 +Subject: kbuild: pacman-pkg: make "rc" releases adhere to pacman versioning + scheme +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Viktor Jägersküpper + +[ Upstream commit 202550713128da20d9381d6d2dc0f6b73839f434 ] + +The package versioning scheme does not enable smooth upgrades from "rc" +releases to the corresponding stable releases (e.g. 7.0.0-rc7 -> 7.0.0) +because pacman considers that a downgrade due to the underscore in +pkgver (e.g. 7.0.0_rc7), see e.g. vercmp(8) for an explanation of the +package version comparison used by pacman. Package versions which are +derived from said releases (e.g. built from git revisions) are +similarly affected. Fix this by modifying pkgver in order to remove the +hyphen from kernel versions containing "-rcN", where N is a +non-negative integer. + +Acked-by: Thomas Weißschuh +Signed-off-by: Viktor Jägersküpper +Reviewed-by: Nathan Chancellor +Tested-by: Nathan Chancellor +Link: https://patch.msgid.link/20260515215913.92481-1-viktor_jaegerskuepper@freenet.de +Fixes: c8578539deba ("kbuild: add script and target to generate pacman package") +Signed-off-by: Nicolas Schier +Signed-off-by: Sasha Levin +--- + scripts/package/PKGBUILD | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD +index dca706617adc7..e7a077d483347 100644 +--- a/scripts/package/PKGBUILD ++++ b/scripts/package/PKGBUILD +@@ -10,7 +10,7 @@ for pkg in $_extrapackages; do + pkgname+=("${pkgbase}-${pkg}") + done + +-pkgver="${KERNELRELEASE//-/_}" ++pkgver="$(echo "${KERNELRELEASE}" | sed 's/-\(rc[0-9]\+\)/\1/;s/-/_/g')" + # The PKGBUILD is evaluated multiple times. + # Running scripts/build-version from here would introduce inconsistencies. + pkgrel="${KBUILD_REVISION}" +-- +2.53.0 + diff --git a/queue-6.12/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch b/queue-6.12/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch new file mode 100644 index 0000000000..6806150f98 --- /dev/null +++ b/queue-6.12/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch @@ -0,0 +1,103 @@ +From e3e98ee0d86595bcdcb48471e5f47f84a617f0e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() + +From: Jianpeng Chang + +[ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ] + +When kprobe_add_area_blacklist() iterates through a section like +.kprobes.text, the start address may not correspond to a named symbol. +On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by +commit baaf553d3bc3 ("arm64: Implement +HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag +-fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry +point for ftrace call_ops. These pre-function NOPs sit at the section base +address, before the first named function symbol. The compiler emits a $x +mapping symbol at offset 0x00 to mark the start of code, but +find_kallsyms_symbol() ignores mapping symbols. + +Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no +pre-function NOPs are inserted, the first function starts at offset +0x00, and the bug does not trigger. + +This only affects modules that have a .kprobes.text section (i.e. those +using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead +(like kretprobe_example.ko) blacklist exact function addresses via the +_kprobe_blacklist section and are not affected. + +For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, +the .kprobes.text section layout is: + + offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) + offset 0x08: handler_post (64 bytes) + offset 0x50: handler_pre (68 bytes) + +kprobe_add_area_blacklist() starts iterating from the section base +address (offset 0x00), which only has the $x mapping symbol. +kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() +for this address, which goes through: + + kallsyms_lookup_size_offset() + -> module_address_lookup() + -> find_kallsyms_symbol() + +find_kallsyms_symbol() scans all module symbols to find the closest +preceding symbol. + +Since no named text symbol exists at offset 0x00, +find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol +whose address is in the temporary image) as the "best" match. The +computed "size" = next_text_symbol - modinfo_symbol spans across +these two unrelated memory regions, creating a blacklist entry with +a bogus range of tens of terabytes. + +Whether this causes a visible failure depends on address randomization, +here is what happens on Raspberry Pi 4/5: + + - On RPi5, the bogus size was ~35 TB. start + size stayed within + 64-bit range, so the blacklist entry covered the entire kernel + text. register_kprobe() in the module's own init function failed + with -EINVAL. + + - On RPi4, the bogus size was ~75 TB. start + size overflowed + 64 bits and wrapped to a small address near zero. The range + check (addr >= start && addr < end) then failed because end + wrapped around, so the bogus entry was accidentally harmless + and kprobes worked by luck. + +The same bug exists on both machines, but randomization determines whether +the integer overflow masks it or not. + +Fix this by adding notrace to the __kprobes macro. Functions in +.kprobes.text are kprobe infrastructure handlers that should never be +traced by ftrace. With notrace, the compiler stops inserting them and the +non-symbol gap at the section start disappears entirely. + +Link: https://lore.kernel.org/all/20260506012706.2785785-1-jianpeng.chang.cn@windriver.com/ + +Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") +Signed-off-by: Jianpeng Chang +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/asm-generic/kprobes.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h +index 060eab094e5a2..5290a2b2e15a0 100644 +--- a/include/asm-generic/kprobes.h ++++ b/include/asm-generic/kprobes.h +@@ -14,7 +14,7 @@ static unsigned long __used \ + _kbl_addr_##fname = (unsigned long)fname; + # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) + /* Use this to forbid a kprobes attach on very low level functions */ +-# define __kprobes __section(".kprobes.text") ++# define __kprobes notrace __section(".kprobes.text") + # define nokprobe_inline __always_inline + #else + # define NOKPROBE_SYMBOL(fname) +-- +2.53.0 + diff --git a/queue-6.12/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-6.12/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..c7811b26cc --- /dev/null +++ b/queue-6.12/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From e3d259818008ca5308690017a0b4a72a5abf4ab4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 34d7242d526dc..8a30ad48f3c07 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-6.12/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-6.12/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..6190bae8d4 --- /dev/null +++ b/queue-6.12/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From 9d84933e13953a3b02825af18ebec82d13acbd85 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 8a30ad48f3c07..e22cbee60ab25 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-6.12/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch b/queue-6.12/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch new file mode 100644 index 0000000000..c6504b4eb7 --- /dev/null +++ b/queue-6.12/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch @@ -0,0 +1,76 @@ +From a4bb3cc0217f23449bb43f6f41ff6a2d0bfff705 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 15:05:07 +0800 +Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions + +From: Tiezhu Yang + +[ Upstream commit 1c856e158fd34ef2c4475a81c1dc386329989938 ] + +KPROBE_HIT_SS and KPROBE_REENTER are two types of fatal recursions that +can not be safely recovered in kprobes. + +KPROBE_HIT_SS means that a kprobe is hit during single-stepping. At +this point, the architecture-specific single-step context is already +active. Nested single-stepping would corrupt the state, as the kprobe +control block (kcb) and hardware registers cannot safely store multiple +levels of stepping state. + +KPROBE_REENTER means that a third-level recursion occurs when a probe +is hit while the system is already handling a nested probe (second- +level). The kcb only provides a single slot (prev_kprobe) to backup the +state. When a third probe is hit, there is no more space to save the +state without corrupting the first-level backup. + +Kprobes work by replacing instructions with breakpoints. In order to +execute the original instruction and continue, it must be moved to a +temporary "single-step" slot. Since there is no backup space left to +set up this slot safely, the CPU would be forced to return to the same +original breakpoint address, triggering an endless loop. + +Currently, the code only prints a warning and returns. This leads to +an infinite re-entry loop as the CPU repeatedly hits the same trap and +a "stuck" CPU core because preemption was disabled at the start of the +handler and never re-enabled in this early return path. + +Fix the logic by: +1. Merging KPROBE_HIT_SS and KPROBE_REENTER cases, as both represent + fatal recursions that cannot be safely recovered. +2. Replacing WARN_ON_ONCE() with BUG() to terminate the system. This + aligns LoongArch with other architectures (x86, arm64, riscv) and + prevents stack overflow while providing diagnostic information. + +Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") +Signed-off-by: Tiezhu Yang +Signed-off-by: Huacai Chen +Signed-off-by: Sasha Levin +--- + arch/loongarch/kernel/kprobes.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c +index 8ba391cfabb00..bd783ebed4cc9 100644 +--- a/arch/loongarch/kernel/kprobes.c ++++ b/arch/loongarch/kernel/kprobes.c +@@ -184,16 +184,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) + { + switch (kcb->kprobe_status) { +- case KPROBE_HIT_SS: + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs, kcb, 1); + break; ++ case KPROBE_HIT_SS: + case KPROBE_REENTER: + pr_warn("Failed to recover from reentered kprobes.\n"); + dump_kprobe(p); +- WARN_ON_ONCE(1); ++ BUG(); + break; + default: + WARN_ON(1); +-- +2.53.0 + diff --git a/queue-6.12/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-6.12/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..1f51bd8f69 --- /dev/null +++ b/queue-6.12/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From 0f1bc6a8a80dcbeee8f5535bb9a74e26b1733def Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index bccc7e7b2a848..f5570061abe7f 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1861,6 +1861,9 @@ static int ag71xx_probe(struct platform_device *pdev) + ag71xx_int_disable(ag, AG71XX_INT_POLL); + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-6.12/net-bridge-flush-multicast-groups-when-snooping-is-d.patch b/queue-6.12/net-bridge-flush-multicast-groups-when-snooping-is-d.patch new file mode 100644 index 0000000000..b0b7703375 --- /dev/null +++ b/queue-6.12/net-bridge-flush-multicast-groups-when-snooping-is-d.patch @@ -0,0 +1,67 @@ +From 3a8f21edc8e1f2c4d00628a10b16f22bfbd3d3f4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Oct 2025 16:45:37 +0200 +Subject: net: bridge: Flush multicast groups when snooping is disabled + +From: Petr Machata + +[ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] + +When forwarding multicast packets, the bridge takes MDB into account when +IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the +MDB is retained, even though it is not used anymore. + +At the same time, during the time that snooping is disabled, the IGMP / MLD +control packets are obviously ignored, and after the snooping is reenabled, +the administrator has to assume it is out of sync. In particular, missed +join and leave messages would lead to traffic being forwarded to wrong +interfaces. + +Keeping the MDB entries around thus serves no purpose, and just takes +memory. Note also that disabling per-VLAN snooping does actually flush the +relevant MDB entries. + +This patch flushes non-permanent MDB entries as global snooping is +disabled. + +Signed-off-by: Petr Machata +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 9bd2914006df7..3d91f5a057509 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4642,6 +4642,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + ++static void br_multicast_del_grps(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_disable_port_ctx(&port->multicast_ctx); ++} ++ + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +@@ -4662,6 +4670,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; ++ br_multicast_del_grps(br); + goto unlock; + } + +-- +2.53.0 + diff --git a/queue-6.12/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-6.12/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..c00e1a9fdc --- /dev/null +++ b/queue-6.12/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From badbd1907cf261c39ce9f7ecf56607b8bf946150 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 93bf085a61d39..7187efb41dbc8 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -828,12 +828,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2353,6 +2357,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2542,6 +2548,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-6.12/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-6.12/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..38994c35ce --- /dev/null +++ b/queue-6.12/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From 89828792f845cb227c8457a95a695bc2d15e9d2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 7187efb41dbc8..21437ce57b64f 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1105,37 +1105,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-6.12/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..40090eebf3 --- /dev/null +++ b/queue-6.12/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 99e323ff030eed1980df3e3bc030c1fee9d32491 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 8fee13bd056ad..2ce8191ad5007 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1444,6 +1445,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1457,7 +1459,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1497,6 +1498,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1507,6 +1509,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1541,6 +1544,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1549,6 +1553,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1558,6 +1563,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1887,6 +1893,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-6.12/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..f508f71453 --- /dev/null +++ b/queue-6.12/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From 1c4b485ecf2b5238ce70e06eb382d659b0f7d365 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 47072fbabcaee..8fee13bd056ad 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1493,6 +1493,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-6.12/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..525a9e75da --- /dev/null +++ b/queue-6.12/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From b730d410c266100c9f60e5be0071bde8a90e2ae2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 92833eefc04b4..47072fbabcaee 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -121,6 +121,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1442,10 +1444,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1499,6 +1501,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1549,6 +1552,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1877,6 +1881,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-6.12/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..b55899b90c --- /dev/null +++ b/queue-6.12/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From f1059955fa740dde38c1b52d50368b2edefa2433 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index 0a21a10a791c5..6b01c44a5f728 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-6.12/net-gro-don-t-merge-zcopy-skbs.patch b/queue-6.12/net-gro-don-t-merge-zcopy-skbs.patch new file mode 100644 index 0000000000..2b3d57bcd8 --- /dev/null +++ b/queue-6.12/net-gro-don-t-merge-zcopy-skbs.patch @@ -0,0 +1,48 @@ +From 8a324902655e7d17d43ce747fdc14831bf19f82c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 22:44:42 +0200 +Subject: net: gro: don't merge zcopy skbs + +From: Sabrina Dubroca + +[ Upstream commit 4db79a322db8c97f7b73b8a347395ef4d685eb40 ] + +skb_gro_receive() can currently copy frags between the source and GRO +skb, without checking the zerocopy status, and in particular the +SKBFL_MANAGED_FRAG_REFS flag. + +When SKBFL_MANAGED_FRAG_REFS is set, the skb doesn't hold a reference +on the pages in shinfo->frags. Appending those frags to another skb's +frags without fixing up the page refcount can lead to UAF. + +When either the last skb in the GRO chain (the one we would append +frags to) or the source skb is zerocopy, don't merge the skbs. + +Fixes: 753f1ca4e1e5 ("net: introduce managed frags infrastructure") +Reported-by: Huzaifa Sidhpurwala +Signed-off-by: Sabrina Dubroca +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/gro.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/gro.c b/net/core/gro.c +index f5c80c2f69df7..e4cebf162efb7 100644 +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -108,6 +108,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) + if (p->pp_recycle != skb->pp_recycle) + return -ETOOMANYREFS; + ++ if (skb_zcopy(p) || skb_zcopy(skb)) ++ return -ETOOMANYREFS; ++ + if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) || + NAPI_GRO_CB(skb)->flush)) + return -E2BIG; +-- +2.53.0 + diff --git a/queue-6.12/net-lan966x-avoid-unregistering-netdev-on-register-f.patch b/queue-6.12/net-lan966x-avoid-unregistering-netdev-on-register-f.patch new file mode 100644 index 0000000000..e10fc166b0 --- /dev/null +++ b/queue-6.12/net-lan966x-avoid-unregistering-netdev-on-register-f.patch @@ -0,0 +1,65 @@ +From bf6333b1aa2c51e7b5039161da24168ec2e30adc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 21:43:11 +0900 +Subject: net: lan966x: avoid unregistering netdev on register failure + +From: Myeonghun Pak + +[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] + +lan966x_probe_port() stores the newly allocated net_device in the +port before calling register_netdev(). If register_netdev() fails, +the probe error path calls lan966x_cleanup_ports(), which sees +port->dev and calls unregister_netdev() for a device that was never +registered. + +Destroy the phylink instance created for this port and clear port->dev +before returning the registration error. The common cleanup path now skips +ports without port->dev before reaching the registered netdev cleanup, so +it only handles ports that reached the registered-netdev lifetime. + +This also avoids treating an uninitialized FDMA netdev and the failed port +as a NULL == NULL match in the common cleanup path. + +Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +index b5dc65a4d6403..9acba25a30586 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +@@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; +- if (!port) ++ if (!port || !port->dev) + continue; + +- if (port->dev) +- unregister_netdev(port->dev); ++ unregister_netdev(port->dev); + + lan966x_xdp_port_deinit(port); + if (lan966x->fdma && lan966x->fdma_ndev == port->dev) +@@ -874,6 +873,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, + err = register_netdev(dev); + if (err) { + dev_err(lan966x->dev, "register_netdev failed\n"); ++ phylink_destroy(phylink); ++ port->phylink = NULL; ++ port->dev = NULL; + return err; + } + +-- +2.53.0 + diff --git a/queue-6.12/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-6.12/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..90f8f126a0 --- /dev/null +++ b/queue-6.12/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 8eed84863bea83b6d45828b5b6179ee330991dff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index e07d0a9529782..f8971844e6d8e 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -76,21 +76,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -219,6 +217,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -237,13 +236,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-6.12/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-6.12/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..59304e4aa3 --- /dev/null +++ b/queue-6.12/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From dbcb77a04256e6cfa721056df95ee6b65c77334f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index f8971844e6d8e..fef0edc90eac9 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -233,6 +233,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-6.12/net-mlx5-do-not-restore-destination-less-tc-rules.patch b/queue-6.12/net-mlx5-do-not-restore-destination-less-tc-rules.patch new file mode 100644 index 0000000000..86c8325a77 --- /dev/null +++ b/queue-6.12/net-mlx5-do-not-restore-destination-less-tc-rules.patch @@ -0,0 +1,55 @@ +From 266155811a73cb5a770f5eb2b443cf11bbe959a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:33:02 +0300 +Subject: net/mlx5: Do not restore destination-less TC rules + +From: Jeroen Massar + +[ Upstream commit 8d0a5af8b1ba598e7340761729801624e7a9330e ] + +After IPsec policy/state TX rules are added, any TC flow rule, which +forwards packets to uplink, is modified to forward to IPsec TX tables. +As these tables are destroyed dynamically, whenever there is no +reference to them, the destinations of this kind of rules must be +restored to uplink, unless there is no destination for that rule. + +The flow rules FLOW_ACTION_ACCEPT, DROP, TRAP, GOTO and SAMPLE do not +have a destination port, and thus out_count = 0. + +At cleanup time of the rules in mlx5_esw_ipsec_modify_flow_dests +we call mlx5_eswitch_restore_ipsec_rule but as the above types +do not have a destination we get an underflow of out_count, as +the port is passed, which is esw_attr->out_count - 1. + +This change avoids calling mlx5_eswitch_restore_ipsec_rule when +there are no output destinations and thus avoids the underflow. + +Fixes: d1569537a837 ("net/mlx5e: Modify and restore TC rules for IPSec TX rules") +Signed-off-by: Jeroen Massar +Reviewed-by: Jianbo Liu +Reviewed-by: Cosmin Ratiu +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +index 4bba2884c1c05..b4fec9d6bff41 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +@@ -142,7 +142,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, + + attr = flow->attr; + esw_attr = attr->esw_attr; +- if (esw_attr->out_count - esw_attr->split_count > 1) ++ if (!esw_attr->out_count || ++ esw_attr->out_count - esw_attr->split_count > 1) + return 0; + + err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, +-- +2.53.0 + diff --git a/queue-6.12/net-phy-dp83tc811-add-reading-of-abilities.patch b/queue-6.12/net-phy-dp83tc811-add-reading-of-abilities.patch new file mode 100644 index 0000000000..a483a0fd72 --- /dev/null +++ b/queue-6.12/net-phy-dp83tc811-add-reading-of-abilities.patch @@ -0,0 +1,40 @@ +From 7fa9934c723ac1c91813d3085feb9b7f3a717a0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 09:19:47 +0200 +Subject: net: phy: DP83TC811: add reading of abilities + +From: Sven Schuchmann + +[ Upstream commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac ] + +At this time the driver is not listing any speeds +it supports. This should be ETHTOOL_LINK_MODE_100baseT1_Full_BIT +for DP83TC811. Add the missing call for phylib to read the abilities. + +Fixes: b753a9faaf9a ("net: phy: DP83TC811: Introduce support for the DP83TC811 phy") +Suggested-by: Andrew Lunn +Signed-off-by: Sven Schuchmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de +[pabeni@redhat.com: dropped revision history] +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/phy/dp83tc811.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c +index 7ea32fb77190c..5425a95352f9f 100644 +--- a/drivers/net/phy/dp83tc811.c ++++ b/drivers/net/phy/dp83tc811.c +@@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { + .config_init = dp83811_config_init, + .config_aneg = dp83811_config_aneg, + .soft_reset = dp83811_phy_reset, ++ .get_features = genphy_c45_pma_read_ext_abilities, + .get_wol = dp83811_get_wol, + .set_wol = dp83811_set_wol, + .config_intr = dp83811_config_intr, +-- +2.53.0 + diff --git a/queue-6.12/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch b/queue-6.12/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch new file mode 100644 index 0000000000..caa90b5bed --- /dev/null +++ b/queue-6.12/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch @@ -0,0 +1,65 @@ +From a6adbecfdf6f8c1a77eff98740c86fe64870004e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 15:26:40 -0700 +Subject: net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint + +From: Xiang Mei + +[ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] + +The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and +smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: + + __string(name, smc->conn.lnk->ibname) + +conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on +these paths already handles this (e.g. !conn->lnk in +SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first +sendmsg()/recvmsg() on an SMC-D socket crashes: + + Oops: general protection fault, probably for non-canonical address + KASAN: null-ptr-deref in range [...] + RIP: 0010:strlen+0x1e/0xa0 + Call Trace: + trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) + smc_rx_recvmsg (net/smc/smc_rx.c:515) + smc_recvmsg (net/smc/af_smc.c:2859) + __sys_recvfrom (net/socket.c:2315) + __x64_sys_recvfrom (net/socket.c:2326) + do_syscall_64 + +The faulting address 0x3e0 is offsetof(struct smc_link, ibname), +confirming the NULL ->lnk deref. Enabling the tracepoint requires +root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has +no capability check, and SMC-D negotiation needs no admin step on +s390 or on x86 with the loopback ISM device loaded. + +Log an empty device name for SMC-D instead of dereferencing NULL. + +Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Dust Li +Reviewed-by: Sidraya Jayagond +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/smc/smc_tracepoint.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h +index a9a6e3c1113aa..53da84f57fd6f 100644 +--- a/net/smc/smc_tracepoint.h ++++ b/net/smc/smc_tracepoint.h +@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, + __field(const void *, smc) + __field(u64, net_cookie) + __field(size_t, len) +- __string(name, smc->conn.lnk->ibname) ++ __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") + ), + + TP_fast_assign( +-- +2.53.0 + diff --git a/queue-6.12/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch b/queue-6.12/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch new file mode 100644 index 0000000000..23822fa6fb --- /dev/null +++ b/queue-6.12/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch @@ -0,0 +1,63 @@ +From bbdf7b82c539b5870fa0c6dd868622fc9705bfa9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:21:38 -0700 +Subject: net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot + +From: Xiang Mei + +[ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] + +On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is +reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() +populates V2 entries starting at index 1, so when no V1 device is +selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == +NULL and ism_chid[0] == 0. + +smc_v2_determine_accepted_chid() then matches the peer's CHID against +the array starting from index 0 using the CHID alone. A malicious +peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches +the empty slot, ini->ism_selected becomes 0, and the subsequent +ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at +offsetof(struct smcd_dev, lgr_lock) == 0x68: + + BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 + Write of size 4 at addr 0000000000000068 by task exploit/144 + Call Trace: + _raw_spin_lock_bh + smc_conn_create (net/smc/smc_core.c:1997) + __smc_connect (net/smc/af_smc.c:1447) + smc_connect (net/smc/af_smc.c:1720) + __sys_connect + __x64_sys_connect + do_syscall_64 + +Require ism_dev[i] to be non-NULL before accepting a CHID match. + +Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") +Reported-by: Weiming Shi +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/smc/af_smc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c +index 23bb360ebd07b..c96abb1386be4 100644 +--- a/net/smc/af_smc.c ++++ b/net/smc/af_smc.c +@@ -1398,7 +1398,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, + int i; + + for (i = 0; i < ini->ism_offered_cnt + 1; i++) { +- if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ++ if (ini->ism_dev[i] && ++ ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + ini->ism_selected = i; + return 0; + } +-- +2.53.0 + diff --git a/queue-6.12/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-6.12/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..e416c6b580 --- /dev/null +++ b/queue-6.12/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From 4d2a55841e4c991bacaea1db7c612925d7cd8a72 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 4550f15d052dc..834cb01f8e0e8 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -800,11 +800,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-6.12/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-6.12/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..f44f665d54 --- /dev/null +++ b/queue-6.12/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From c8c33b5ed66d633645385b733572ce1ab6225d1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 834cb01f8e0e8..7511cce76fbbf 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -789,21 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-6.12/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-6.12/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..8e899882eb --- /dev/null +++ b/queue-6.12/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From 0f0858de8fc62dd00bbf0a9a2edb6ed1b8f350fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index 6240bb2b5b5b7..d480a91f081d3 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.12/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-6.12/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..3da0aa7c39 --- /dev/null +++ b/queue-6.12/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From 4fecde4351a36da95d8d5db39d90cc979ea18278 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index e6f9e343b41f1..f05c79f215ea0 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 02b6501c15a5e..0fc03b07e62ae 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 9985a82555c41..8a10375d89099 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-6.12/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-6.12/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..5b77f8aaa5 --- /dev/null +++ b/queue-6.12/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From 7957ef5e91f977c4d26639c0342b921e5d1ba667 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 7413602195525..e6f9e343b41f1 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index dacd81b12e626..02b6501c15a5e 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 0f2a8c6118d42..9985a82555c41 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index a461c59ad2859..6240bb2b5b5b7 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-6.12/netfilter-exclude-legacy-tables-on-preempt_rt.patch b/queue-6.12/netfilter-exclude-legacy-tables-on-preempt_rt.patch new file mode 100644 index 0000000000..8d6c2a65ee --- /dev/null +++ b/queue-6.12/netfilter-exclude-legacy-tables-on-preempt_rt.patch @@ -0,0 +1,335 @@ +From a824fb74dd93049c912c324a788fbb27121fb501 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Jun 2025 17:44:23 +0200 +Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT. + +From: Pablo Neira Ayuso + +[ Upstream commit 9fce66583f06c212e95e4b76dd61d8432ffa56b6 ] + +The seqcount xt_recseq is used to synchronize the replacement of +xt_table::private in xt_replace_table() against all readers such as +ipt_do_table() + +To ensure that there is only one writer, the writing side disables +bottom halves. The sequence counter can be acquired recursively. Only the +first invocation modifies the sequence counter (signaling that a writer +is in progress) while the following (recursive) writer does not modify +the counter. +The lack of a proper locking mechanism for the sequence counter can lead +to live lock on PREEMPT_RT if the high prior reader preempts the +writer. Additionally if the per-CPU lock on PREEMPT_RT is removed from +local_bh_disable() then there is no synchronisation for the per-CPU +sequence counter. + +The affected code is "just" the legacy netfilter code which is replaced +by "netfilter tables". That code can be disabled without sacrificing +functionality because everything is provided by the newer +implementation. This will only requires the usage of the "-nft" tools +instead of the "-legacy" ones. +The long term plan is to remove the legacy code so lets accelerate the +progress. + +Relax dependencies on iptables legacy, replace select with depends on, +this should cause no harm to existing kernel configs and users can still +toggle IP{6}_NF_IPTABLES_LEGACY in any case. +Make EBTABLES_LEGACY, IPTABLES_LEGACY and ARPTABLES depend on +NETFILTER_XTABLES_LEGACY. Hide xt_recseq and its users, +xt_register_table() and xt_percpu_counter_alloc() behind +NETFILTER_XTABLES_LEGACY. Let NETFILTER_XTABLES_LEGACY depend on +!PREEMPT_RT. + +This will break selftest expecing the legacy options enabled and will be +addressed in a following patch. + +Co-developed-by: Florian Westphal +Co-developed-by: Sebastian Andrzej Siewior +Signed-off-by: Sebastian Andrzej Siewior +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 10 +++++----- + net/ipv4/netfilter/Kconfig | 24 ++++++++++++------------ + net/ipv6/netfilter/Kconfig | 19 +++++++++---------- + net/netfilter/Kconfig | 10 ++++++++++ + net/netfilter/x_tables.c | 16 +++++++++++----- + 5 files changed, 47 insertions(+), 32 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index f16bbbbb94817..60f28e4fb5c0a 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -42,8 +42,8 @@ config NF_CONNTRACK_BRIDGE + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY + tristate "Legacy EBTABLES support" +- depends on BRIDGE && NETFILTER_XTABLES +- default n ++ depends on BRIDGE && NETFILTER_XTABLES_LEGACY ++ default n + help + Legacy ebtables packet/frame classifier. + This is not needed if you are using ebtables over nftables +@@ -65,7 +65,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -76,7 +76,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -86,7 +86,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index ef8009281da5c..2c438b140e88f 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -13,8 +13,8 @@ config NF_DEFRAG_IPV4 + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY + tristate "Legacy IP tables support" +- default n +- select NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + iptables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -182,8 +182,8 @@ config IP_NF_MATCH_TTL + # `filter', generic and specific targets + config IP_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -220,10 +220,10 @@ config IP_NF_TARGET_SYNPROXY + config IP_NF_NAT + tristate "iptables NAT support" + depends on NF_CONNTRACK ++ depends on IP_NF_IPTABLES_LEGACY + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -263,8 +263,8 @@ endif # IP_NF_NAT + # mangle + specific targets + config IP_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -299,7 +299,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -313,7 +313,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -325,8 +325,8 @@ endif # IP_NF_IPTABLES + # ARP tables + config IP_NF_ARPTABLES + tristate "Legacy ARPTABLES support" +- depends on NETFILTER_XTABLES +- default n ++ depends on NETFILTER_XTABLES_LEGACY ++ default n + help + arptables is a legacy packet classifier. + This is not needed if you are using arptables over nftables +@@ -342,7 +342,7 @@ config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES + select NETFILTER_FAMILY_ARP +- depends on NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index e087a8e97ba78..276860f65baae 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -9,9 +9,8 @@ menu "IPv6: Netfilter Configuration" + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY + tristate "Legacy IP6 tables support" +- depends on INET && IPV6 +- select NETFILTER_XTABLES +- default n ++ depends on INET && IPV6 && NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + ip6tables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -196,8 +195,8 @@ config IP6_NF_TARGET_HL + + config IP6_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + tristate + help + Packet filtering defines a table `filter', which has a series of +@@ -233,8 +232,8 @@ config IP6_NF_TARGET_SYNPROXY + + config IP6_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -244,7 +243,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -258,7 +257,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -269,8 +268,8 @@ config IP6_NF_NAT + tristate "ip6tables NAT support" + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED ++ depends on IP6_NF_IPTABLES_LEGACY + select NF_NAT +- select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index df2dc21304efb..0d1d997abe191 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -762,6 +762,16 @@ config NETFILTER_XTABLES_COMPAT + + If unsure, say N. + ++config NETFILTER_XTABLES_LEGACY ++ bool "Netfilter legacy tables support" ++ depends on !PREEMPT_RT ++ help ++ Say Y here if you still require support for legacy tables. This is ++ required by the legacy tools (iptables-legacy) and is not needed if ++ you use iptables over nftables (iptables-nft). ++ Legacy support is not limited to IP, it also includes EBTABLES and ++ ARPTABLES. ++ + comment "Xtables combined modules" + + config NETFILTER_XT_MARK +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index efe7b7d71e7f7..1ca4fa9d249b8 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1340,12 +1340,13 @@ void xt_compat_unlock(u_int8_t af) + EXPORT_SYMBOL_GPL(xt_compat_unlock); + #endif + +-DEFINE_PER_CPU(seqcount_t, xt_recseq); +-EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); +- + struct static_key xt_tee_enabled __read_mostly; + EXPORT_SYMBOL_GPL(xt_tee_enabled); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY ++DEFINE_PER_CPU(seqcount_t, xt_recseq); ++EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); ++ + static int xt_jumpstack_alloc(struct xt_table_info *i) + { + unsigned int size; +@@ -1537,6 +1538,7 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++#endif + + #ifdef CONFIG_PROC_FS + static void *xt_table_seq_start(struct seq_file *seq, loff_t *pos) +@@ -1920,6 +1922,7 @@ void xt_proto_fini(struct net *net, u_int8_t af) + } + EXPORT_SYMBOL_GPL(xt_proto_fini); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY + /** + * xt_percpu_counter_alloc - allocate x_tables rule counter + * +@@ -1974,6 +1977,7 @@ void xt_percpu_counter_free(struct xt_counters *counters) + free_percpu((void __percpu *)pcnt); + } + EXPORT_SYMBOL_GPL(xt_percpu_counter_free); ++#endif + + static int __net_init xt_net_init(struct net *net) + { +@@ -2006,8 +2010,10 @@ static int __init xt_init(void) + unsigned int i; + int rv; + +- for_each_possible_cpu(i) { +- seqcount_init(&per_cpu(xt_recseq, i)); ++ if (IS_ENABLED(CONFIG_NETFILTER_XTABLES_LEGACY)) { ++ for_each_possible_cpu(i) { ++ seqcount_init(&per_cpu(xt_recseq, i)); ++ } + } + + xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.12/netfilter-make-legacy-configs-user-selectable.patch b/queue-6.12/netfilter-make-legacy-configs-user-selectable.patch new file mode 100644 index 0000000000..b8c4ae9d36 --- /dev/null +++ b/queue-6.12/netfilter-make-legacy-configs-user-selectable.patch @@ -0,0 +1,104 @@ +From 8d07f48651e8c171d698b0ce36915aaa96085d27 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Sep 2024 02:58:54 -0700 +Subject: netfilter: Make legacy configs user selectable + +From: Breno Leitao + +[ Upstream commit 6c959fd5e17387201dba3619b2e6af213939a0a7 ] + +This option makes legacy Netfilter Kconfig user selectable, giving users +the option to configure iptables without enabling any other config. + +Make the following KConfig entries user selectable: + * BRIDGE_NF_EBTABLES_LEGACY + * IP_NF_ARPTABLES + * IP_NF_IPTABLES_LEGACY + * IP6_NF_IPTABLES_LEGACY + +Signed-off-by: Breno Leitao +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 8 +++++++- + net/ipv4/netfilter/Kconfig | 16 ++++++++++++++-- + net/ipv6/netfilter/Kconfig | 9 ++++++++- + 3 files changed, 29 insertions(+), 4 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 104c0125e32e8..f16bbbbb94817 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -41,7 +41,13 @@ config NF_CONNTRACK_BRIDGE + + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY +- tristate ++ tristate "Legacy EBTABLES support" ++ depends on BRIDGE && NETFILTER_XTABLES ++ default n ++ help ++ Legacy ebtables packet/frame classifier. ++ This is not needed if you are using ebtables over nftables ++ (iptables-nft). + + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 1b991b889506a..ef8009281da5c 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -12,7 +12,13 @@ config NF_DEFRAG_IPV4 + + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP tables support" ++ default n ++ select NETFILTER_XTABLES ++ help ++ iptables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" +@@ -318,7 +324,13 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate ++ tristate "Legacy ARPTABLES support" ++ depends on NETFILTER_XTABLES ++ default n ++ help ++ arptables is a legacy packet classifier. ++ This is not needed if you are using arptables over nftables ++ (iptables-nft). + + config NFT_COMPAT_ARP + tristate +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index f3c8e2d918e13..e087a8e97ba78 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -8,7 +8,14 @@ menu "IPv6: Netfilter Configuration" + + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP6 tables support" ++ depends on INET && IPV6 ++ select NETFILTER_XTABLES ++ default n ++ help ++ ip6tables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" +-- +2.53.0 + diff --git a/queue-6.12/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-6.12/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..683a920cc1 --- /dev/null +++ b/queue-6.12/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From e0da8f8aafbbc0d4f4829171a14bb9607bce7aef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 5897f3dbaf7c3..df2022fe440b0 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -310,6 +310,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 132b0e4a6d4df..13593391d6058 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 8b8885a73c764..c6d5b927830dd 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 97ead883e4a13..d19fce8589809 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 3d101613f27fa..49b7989c24e08 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1789,14 +1789,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1887,7 +1879,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 595bfb492b1c1..0dea754a91209 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index db90db7057cc4..4d3b124923080 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index a5db7c67d61be..d6c5824943f8e 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index b46a790917306..6f7afec7954bd 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 2b89adc1e5751..81175c20ccbe8 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 7d5602950ae72..6b431b3f90ddb 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1795,14 +1795,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1894,7 +1886,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 9dcd4501fe800..cf561919bde84 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index ce2cbce9e3ed3..1a758f2bc5379 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index e119d4f090cc8..4ce45f3d11109 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 8af0f8bd036dc..923455921c1dd 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 66018b169b010..c44834d93fc79 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 1ca4fa9d249b8..2d93f189a79b9 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1538,6 +1538,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-6.12/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-6.12/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..fec3b0d730 --- /dev/null +++ b/queue-6.12/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From f47da49deed52dbdd3d10f928793fb2be6c4f9cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index df2022fe440b0..706f08839050a 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -309,8 +309,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index d19fce8589809..f3dadbc416a3a 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 49b7989c24e08..84b1f49ddbc5c 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1791,7 +1790,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index d6c5824943f8e..4dae3da4586b8 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 6b431b3f90ddb..1eac22dbb957c 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1797,7 +1796,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 4ce45f3d11109..8088ebaf9b352 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 2d93f189a79b9..76fd0999db4a8 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1522,23 +1525,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1548,6 +1534,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1557,6 +1551,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1567,6 +1562,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2013,8 +2052,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2023,8 +2064,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-6.12/netfilter-x_tables-close-dangling-table-module-init-.patch b/queue-6.12/netfilter-x_tables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..4e681409a9 --- /dev/null +++ b/queue-6.12/netfilter-x_tables-close-dangling-table-module-init-.patch @@ -0,0 +1,406 @@ +From 56e2255297c4fab7ff8d74a2a60ff039f83ad9c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:20 +0200 +Subject: netfilter: x_tables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] + +Similar to the previous ebtables patch: +template add exposes the table to userspace, we must do this last to +rnsure the pernet ops are set up (contain the destructors). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ + net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- + net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- + net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- + net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- + 9 files changed, 105 insertions(+), 99 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 382345567a600..370b635e3523b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { + + static int __init arptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- arptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); +- if (IS_ERR(arpfilter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(arpfilter_ops)) + return PTR_ERR(arpfilter_ops); +- } + + ret = register_pernet_subsys(&arptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ arptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(arpfilter_ops); +- return ret; ++ unregister_pernet_subsys(&arptable_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(arpfilter_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 0dea754a91209..672d7da1071d3 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { + + static int __init iptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- iptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&iptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ iptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_filter_net_ops); ++ goto err_free; + } + + return 0; ++err_free: ++ kfree(filter_ops); ++ return ret; + } + + static void __exit iptable_filter_fini(void) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 4d3b124923080..13d25d9a4610e 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -111,25 +111,26 @@ static struct pernet_operations iptable_mangle_net_ops = { + + static int __init iptable_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- iptable_mangle_table_init); +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); +- ret = PTR_ERR(mangle_ops); +- return ret; +- } ++ if (IS_ERR(mangle_ops)) ++ return PTR_ERR(mangle_ops); + + ret = register_pernet_subsys(&iptable_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ iptable_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 6f7afec7954bd..2745c22f4034d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, +- iptable_raw_table_init); +- if (ret < 0) +- return ret; +- + rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&iptable_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ++ iptable_raw_table_init); + if (ret < 0) { +- xt_unregister_template(table); +- kfree(rawtable_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 81175c20ccbe8..491894511c544 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { + + static int __init iptable_security_init(void) + { +- int ret = xt_register_template(&security_table, +- iptable_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&iptable_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ iptable_security_table_init); + if (ret < 0) { +- xt_unregister_template(&security_table); +- kfree(sectbl_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index cf561919bde84..b074fc4776764 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { + + static int __init ip6table_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- ip6table_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&ip6table_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ip6table_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(filter_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 1a758f2bc5379..e6ee036a9b2c5 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -104,25 +104,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { + + static int __init ip6table_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- ip6table_mangle_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); ++ if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); +- } + + ret = register_pernet_subsys(&ip6table_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ ip6table_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 923455921c1dd..3b161ee875bcc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, ip6table_raw_table_init); +- if (ret < 0) +- return ret; +- + /* Register hooks */ + rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&ip6table_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ip6table_raw_table_init); + if (ret < 0) { +- kfree(rawtable_ops); +- xt_unregister_template(table); +- return ret; ++ unregister_pernet_subsys(&ip6table_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index c44834d93fc79..4bd5d97b8ab65 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { + + static int __init ip6table_security_init(void) + { +- int ret = xt_register_template(&security_table, +- ip6table_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&ip6table_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ ip6table_security_table_init); + if (ret < 0) { +- kfree(sectbl_ops); +- xt_unregister_template(&security_table); +- return ret; ++ unregister_pernet_subsys(&ip6table_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.12/netfilter-x_tables-unregister-the-templates-first.patch b/queue-6.12/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..d81cf9b9d7 --- /dev/null +++ b/queue-6.12/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From 5852619bba7aaf111efc6a95ac81b2c86367343f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 78cd5ee24448f..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 3ab908b747951..595bfb492b1c1 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 385d945d8ebea..db90db7057cc4 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -135,8 +135,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 0e7f53964d0af..b46a790917306 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index d885443cb2679..2b89adc1e5751 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index e8992693e14a0..9dcd4501fe800 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 8dd4cd0c47bd4..ce2cbce9e3ed3 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -128,8 +128,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index fc9f6754028f2..8af0f8bd036dc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 4df14a9bae782..66018b169b010 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-6.12/netfs-defer-the-emission-of-trace_netfs_folio.patch b/queue-6.12/netfs-defer-the-emission-of-trace_netfs_folio.patch new file mode 100644 index 0000000000..119329d87f --- /dev/null +++ b/queue-6.12/netfs-defer-the-emission-of-trace_netfs_folio.patch @@ -0,0 +1,116 @@ +From 3e8a97aaba4e30162079ff2140d888d521251bda Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:49 +0100 +Subject: netfs: Defer the emission of trace_netfs_folio() + +From: David Howells + +[ Upstream commit daeb443b92817021c1234e8eded219e164b7c35d ] + +Change netfs_perform_write() to keep the netfs_folio trace value in a +variable and emit it later to make it easier to choose the value displayed. +This is a prerequisite for a subsequent patch. + +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten") +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index be77a137cc871..48c66d26e7b7e 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -143,6 +143,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + do { ++ enum netfs_folio_trace trace; + struct netfs_folio *finfo; + struct netfs_group *group; + unsigned long long fpos; +@@ -216,7 +217,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_folio_is_uptodate); ++ trace = netfs_folio_is_uptodate; + goto copied; + } + +@@ -232,7 +233,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + zero_user_segment(&folio->page, offset + copied, flen); + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_modify_and_clear); ++ trace = netfs_modify_and_clear; + goto copied; + } + +@@ -250,7 +251,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_whole_folio_modify); ++ trace = netfs_whole_folio_modify; + goto copied; + } + +@@ -277,7 +278,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_just_prefetch); ++ trace = netfs_just_prefetch; + goto copied; + } + +@@ -291,7 +292,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (offset == 0 && copied == flen) { + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_streaming_filled_page); ++ trace = netfs_streaming_filled_page; + goto copied; + } + +@@ -306,7 +307,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len = copied; + folio_attach_private(folio, (void *)((unsigned long)finfo | + NETFS_FOLIO_INFO)); +- trace_netfs_folio(folio, netfs_streaming_write); ++ trace = netfs_streaming_write; + goto copied; + } + +@@ -326,9 +327,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + folio_detach_private(folio); + folio_mark_uptodate(folio); + kfree(finfo); +- trace_netfs_folio(folio, netfs_streaming_cont_filled_page); ++ trace = netfs_streaming_cont_filled_page; + } else { +- trace_netfs_folio(folio, netfs_streaming_write_cont); ++ trace = netfs_streaming_write_cont; + } + goto copied; + } +@@ -344,6 +345,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + continue; + + copied: ++ trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); + + /* Update the inode size if we moved the EOF marker */ +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-a-few-minor-bugs-in-netfs_page_mkwrite.patch b/queue-6.12/netfs-fix-a-few-minor-bugs-in-netfs_page_mkwrite.patch new file mode 100644 index 0000000000..3110edc0f4 --- /dev/null +++ b/queue-6.12/netfs-fix-a-few-minor-bugs-in-netfs_page_mkwrite.patch @@ -0,0 +1,90 @@ +From dacd44a94f757de279ac9b107b5b6fa3bc176bf0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 5 Oct 2024 19:23:04 +0100 +Subject: netfs: Fix a few minor bugs in netfs_page_mkwrite() + +From: Matthew Wilcox (Oracle) + +[ Upstream commit c6a90fe7f080d71271b723490454cfda1f81e4b0 ] + +We can't return with VM_FAULT_SIGBUS | VM_FAULT_LOCKED; the core +code will not unlock the folio in this instance. Introduce a new +"unlock" error exit to handle this case. Use it to handle +the "folio is truncated" check, and change the "writeback interrupted +by a fatal signal" to do a NOPAGE exit instead of letting the core +code install the folio currently under writeback before killing the +process. + +Signed-off-by: Matthew Wilcox (Oracle) +Link: https://lore.kernel.org/r/20241005182307.3190401-3-willy@infradead.org +Signed-off-by: Christian Brauner +Stable-dep-of: ccde2ac757c7 ("netfs: Fix folio->private handling in netfs_perform_write()") +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 27 +++++++++++++-------------- + 1 file changed, 13 insertions(+), 14 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 08da4c2512f52..a02bd071cee77 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -513,7 +513,9 @@ EXPORT_SYMBOL(netfs_file_write_iter); + + /* + * Notification that a previously read-only page is about to become writable. +- * Note that the caller indicates a single page of a multipage folio. ++ * The caller indicates the precise page that needs to be written to, but ++ * we only track group on a per-folio basis, so we block more often than ++ * we might otherwise. + */ + vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_group) + { +@@ -523,7 +525,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + struct address_space *mapping = file->f_mapping; + struct inode *inode = file_inode(file); + struct netfs_inode *ictx = netfs_inode(inode); +- vm_fault_t ret = VM_FAULT_RETRY; ++ vm_fault_t ret = VM_FAULT_NOPAGE; + int err; + + _enter("%lx", folio->index); +@@ -532,21 +534,15 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + + if (folio_lock_killable(folio) < 0) + goto out; +- if (folio->mapping != mapping) { +- folio_unlock(folio); +- ret = VM_FAULT_NOPAGE; +- goto out; +- } +- +- if (folio_wait_writeback_killable(folio)) { +- ret = VM_FAULT_LOCKED; +- goto out; +- } ++ if (folio->mapping != mapping) ++ goto unlock; ++ if (folio_wait_writeback_killable(folio) < 0) ++ goto unlock; + + /* Can we see a streaming write here? */ + if (WARN_ON(!folio_test_uptodate(folio))) { +- ret = VM_FAULT_SIGBUS | VM_FAULT_LOCKED; +- goto out; ++ ret = VM_FAULT_SIGBUS; ++ goto unlock; + } + + group = netfs_folio_group(folio); +@@ -581,5 +577,8 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + out: + sb_end_pagefault(inode->i_sb); + return ret; ++unlock: ++ folio_unlock(folio); ++ goto out; + } + EXPORT_SYMBOL(netfs_page_mkwrite); +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch b/queue-6.12/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch new file mode 100644 index 0000000000..94172c0013 --- /dev/null +++ b/queue-6.12/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch @@ -0,0 +1,80 @@ +From 3e649a3a2731585852ceb4c33e8e4059e5fd3176 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:54 +0100 +Subject: netfs: Fix early put of sink folio in netfs_read_gaps() + +From: David Howells + +[ Upstream commit 3e5dd91b87a8b1450217b56a336bee315f40da7d ] + +Fix netfs_read_gaps() to release the sink page it uses after waiting for +the request to complete. The way the sink page is used is that an +ITER_BVEC-class iterator is created that has the gaps from the target folio +at either end, but has the sink page tiled over the middle so that a single +read op can fill in both gaps. + +The bug was found by KASAN detecting a UAF on the generic/075 xfstest in +the cifsd kernel thread that handles reception of data from the TCP socket: + + BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 + Write of size 885 at addr ffff888107f92000 by task cifsd/1285 + CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) + Call Trace: + dump_stack_lvl+0x5d/0x80 + print_report+0x17f/0x4f1 + kasan_report+0x100/0x1e0 + kasan_check_range+0x10f/0x1e0 + __asan_memcpy+0x3c/0x60 + _copy_to_iter+0x48a/0xa20 + __skb_datagram_iter+0x2c9/0x430 + skb_copy_datagram_iter+0x6e/0x160 + tcp_recvmsg_locked+0xce0/0x1130 + tcp_recvmsg+0xeb/0x300 + inet_recvmsg+0xcf/0x3a0 + sock_recvmsg+0xea/0x100 + cifs_readv_from_socket+0x3a6/0x4d0 [cifs] + cifs_read_iter_from_socket+0xdd/0x130 [cifs] + cifs_readv_receive+0xaad/0xb10 [cifs] + cifs_demultiplex_thread+0x1148/0x1740 [cifs] + kthread+0x1cf/0x210 + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Reported-by: Steve French +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 2dd2260352dbf..1c906035fef02 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -525,14 +525,14 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + + netfs_read_to_pagecache(rreq); + +- if (sink) +- folio_put(sink); +- + ret = netfs_wait_for_read(rreq); + if (ret == 0) { + flush_dcache_folio(folio); + folio_mark_uptodate(folio); + } ++ ++ if (sink) ++ folio_put(sink); + folio_unlock(folio); + netfs_put_request(rreq, false, netfs_rreq_trace_put_return); + return ret < 0 ? ret : 0; +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch b/queue-6.12/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch new file mode 100644 index 0000000000..c736765203 --- /dev/null +++ b/queue-6.12/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch @@ -0,0 +1,307 @@ +From 8cbd24bf657525c04658b68ced25adfccebd3954 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:58 +0100 +Subject: netfs: Fix folio->private handling in netfs_perform_write() + +From: David Howells + +[ Upstream commit ccde2ac757c713535b224233a296de40efe5212d ] + +Under some circumstances, netfs_perform_write() doesn't correctly +manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing +to a group and pointing to a netfs_folio struct, leading to potential +multiple attachments of private data with associated folio ref leaks and +also leaks of netfs_folio structs or netfs_group refs. + +Fix this by consolidating the place at which a folio is marked uptodate in +one place and having that look at what's attached to folio->private and +decide how to clean it up and then set the new group. Also, the content +shouldn't be flushed if group is NULL, even if a group is specified in the +netfs_group parameter, as that would be the case for a new folio. A +filesystem should always specify netfs_group or never specify netfs_group. + +The Sashiko auto-review tool noted that it was theoretically possible that +the fpos >= ctx->zero_point section might leak if it modified a streaming +write folio. This is unlikely, but with a network filesystem, third party +changes can happen. It also pointed out that __netfs_set_group() would +leak if called multiple times on the same folio from the "whole folio +modify section". + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 134 +++++++++++++++++++++-------------- + include/trace/events/netfs.h | 1 + + 2 files changed, 82 insertions(+), 53 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 8ba74556bccab..f4e9d88a0a7bf 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -13,24 +13,6 @@ + #include + #include "internal.h" + +-static void __netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- if (netfs_group) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +-} +- +-static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- void *priv = folio_get_private(folio); +- +- if (unlikely(priv != netfs_group)) { +- if (netfs_group && (!priv || priv == NETFS_FOLIO_COPY_TO_CACHE)) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +- else if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) +- folio_detach_private(folio); +- } +-} +- + /* + * Grab a folio for writing and lock it. Attempt to allocate as large a folio + * as possible to hold as much of the remaining length as possible in one go. +@@ -151,6 +133,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + size_t offset; /* Offset into pagecache folio */ + size_t part; /* Bytes to write to folio */ + size_t copied; /* Bytes copied from user */ ++ void *priv; + + offset = pos & (max_chunk - 1); + part = min(max_chunk - offset, iov_iter_count(iter)); +@@ -196,6 +179,25 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto error_folio_unlock; + } + ++ finfo = netfs_folio_info(folio); ++ group = netfs_folio_group(folio); ++ ++ /* If the requested group differs from the group set on the ++ * page, then we need to flush out the folio if it has a group ++ * set (ie. is non-NULL). Note that COPY_TO_CACHE is a special ++ * case, being a netfs annotation rather than an actual group. ++ * ++ * The filesystem isn't permitted to mix writes with groups and ++ * writes without groups as the NULL group is used to indicate ++ * that no group is set. ++ */ ++ if (unlikely(group != netfs_group) && ++ group != NETFS_FOLIO_COPY_TO_CACHE && ++ group) { ++ WARN_ON_ONCE(!netfs_group); ++ goto flush_content; ++ } ++ + /* Decide how we should modify a folio. We might be attempting + * to do write-streaming, as we don't want to a local RMW cycle + * if we can avoid it. If we're doing local caching or content +@@ -203,22 +205,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + * file is open readably, then we let ->read_folio() fill in + * the gaps. + */ +- finfo = netfs_folio_info(folio); +- group = netfs_folio_group(folio); +- +- if (unlikely(group != netfs_group) && +- group != NETFS_FOLIO_COPY_TO_CACHE) +- goto flush_content; +- + if (folio_test_uptodate(folio)) { + if (mapping_writably_mapped(mapping)) + flush_dcache_folio(folio); + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_folio_is_uptodate; +- goto copied; ++ goto copied_uptodate; + } + + /* If the page is above the zero-point then we assume that the +@@ -231,24 +225,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + folio_zero_segment(folio, offset + copied, flen); +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_modify_and_clear; +- goto copied; ++ if (finfo) ++ trace = netfs_modify_and_clear_rm_finfo; ++ else ++ trace = netfs_modify_and_clear; ++ goto mark_uptodate; + } + + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (likely(copied == part)) { +- if (finfo) { ++ if (finfo) + trace = netfs_whole_folio_modify_filled; +- goto folio_now_filled; +- } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; +- goto copied; ++ else ++ trace = netfs_whole_folio_modify; ++ goto mark_uptodate; + } + if (copied == 0) + goto copy_failed; +@@ -266,7 +258,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += finfo->dirty_offset; + if (finfo->dirty_len == flen) { + trace = netfs_whole_folio_modify_filled_efault; +- goto folio_now_filled; ++ goto mark_uptodate; + } + if (copied > finfo->dirty_len) + finfo->dirty_len = copied; +@@ -294,11 +286,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_just_prefetch; +- goto copied; ++ goto copied_uptodate; + } + ++ /* Do a streaming write on a folio that has nothing in it yet. */ + if (!finfo) { + ret = -EIO; + if (WARN_ON(folio_get_private(folio))) +@@ -307,10 +299,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + if (offset == 0 && copied == flen) { +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); + trace = netfs_streaming_filled_page; +- goto copied; ++ goto mark_uptodate; + } + + finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); +@@ -339,7 +329,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { + trace = netfs_streaming_cont_filled_page; +- goto folio_now_filled; ++ goto mark_uptodate; + } + trace = netfs_streaming_write_cont; + goto copied; +@@ -355,13 +345,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + +- folio_now_filled: +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); ++ /* Mark a folio as being up to data when we've filled it ++ * completely. If the folio has a group attached, then it must ++ * be the same group, otherwise we should have flushed it out ++ * above. We have to get rid of the netfs_folio struct if ++ * there was one. ++ */ ++ mark_uptodate: + folio_mark_uptodate(folio); +- kfree(finfo); ++ ++ copied_uptodate: ++ priv = folio_get_private(folio); ++ if (likely(priv == netfs_group)) { ++ /* Already set correctly; no change required. */ ++ } else if (priv == NETFS_FOLIO_COPY_TO_CACHE) { ++ if (!netfs_group) ++ folio_detach_private(folio); ++ else ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ } else if (!priv) { ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ } else { ++ WARN_ON_ONCE(!finfo); ++ if (netfs_group) ++ /* finfo->netfs_group has a ref */ ++ folio_change_private(folio, netfs_group); ++ else ++ folio_detach_private(folio); ++ kfree(finfo); ++ } ++ + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +@@ -526,6 +539,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + struct inode *inode = file_inode(file); + struct netfs_inode *ictx = netfs_inode(inode); + vm_fault_t ret = VM_FAULT_NOPAGE; ++ void *priv; + int err; + + _enter("%lx", folio->index); +@@ -546,7 +560,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + } + + group = netfs_folio_group(folio); +- if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) { ++ if (group && ++ group != netfs_group && ++ group != NETFS_FOLIO_COPY_TO_CACHE) { + folio_unlock(folio); + err = filemap_fdatawrite_range(mapping, + folio_pos(folio), +@@ -568,7 +584,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus); + else + trace_netfs_folio(folio, netfs_folio_trace_mkwrite); +- netfs_set_group(folio, netfs_group); ++ ++ priv = folio_get_private(folio); ++ if (priv != netfs_group) { ++ if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_detach_private(folio); ++ else if (netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ else if (netfs_group && !priv) ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ else ++ WARN_ON_ONCE(1); ++ } ++ + file_update_time(file); + set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); + if (ictx->ops->post_modify) +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index 6395827e83954..1d9b068bb1758 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -149,6 +149,7 @@ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ ++ EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ + EM(netfs_flush_content, "flush") \ +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch b/queue-6.12/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch new file mode 100644 index 0000000000..51f33010ef --- /dev/null +++ b/queue-6.12/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch @@ -0,0 +1,128 @@ +From e5bfebc1db2d6d03ab72faaaad794cc126bb6e2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:48 +0100 +Subject: netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes + gone + +From: David Howells + +[ Upstream commit 156ac2ec2ee77c44c4eb7439d6d165247ba12247 ] + +If a streaming write is made, this will leave the relevant modified folio +in a not-uptodate, but dirty state with a netfs_folio struct hung off of +folio->private indicating the dirty range. Subsequently truncating the +file such that the dirty data in the folio is removed, but the first part +of the folio theoretically remains will cause the netfs_folio struct to be +discarded... but will leave the dirty flag set. + +If the folio is then read via mmap(), netfs_read_folio() will see that the +page is dirty and jump to netfs_read_gaps() to fill in the missing bits. +netfs_read_gaps(), however, expects there to be a netfs_folio struct +present and can oops because truncate removed it. + +Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the +event that all the dirty data in the folio is erased (as nfs does). + +Also add some tracepoints to log modifications to a dirty page. + +This can be reproduced with something like: + + dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1 + umount /xfstest.test + mount /xfstest.test + xfs_io -c "w 0xbbbf 0xf96c" \ + -c "truncate 0xbbbf" \ + -c "mmap -r 0xb000 0x11000" \ + -c "mr 0xb000 0x11000" \ + /xfstest.test/foo + +with fscaching disabled (otherwise streaming writes are suppressed) and a +change to netfs_perform_write() to disallow streaming writes if the fd is +open O_RDWR: + + if (//(file->f_mode & FMODE_READ) || <--- comment this out + netfs_is_cache_enabled(ctx)) { + +It should be reproducible even without this change, but if prevents the +above trivial xfs_io command from reproducing it. + +Note that the initial dd is important: the file must start out sufficiently +large that the zero-point logic doesn't just clear the gaps because it +knows there's nothing in the file to read yet. Unmounting and mounting is +needed to clear the pagecache (there are other ways to do that that may +also work). + +This was initially reproduced with the generic/522 xfstest on some patches +that remove the FMODE_READ restriction. + +Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write") +Reported-by: Marc Dionne +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 6 +++++- + include/trace/events/netfs.h | 4 ++++ + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 78fe5796b2b2f..488a4b1914300 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -268,6 +268,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; + finfo->dirty_offset = offset; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } + +@@ -276,12 +277,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + */ + if (iend >= fend) { + finfo->dirty_len = offset - fstart; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_tail); + return; + } + + /* A partial write was split. The caller has already zeroed + * it, so just absorb the hole. + */ ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_middle); + } + return; + +@@ -289,8 +292,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + netfs_put_group(netfs_folio_group(folio)); + folio_detach_private(folio); + folio_clear_uptodate(folio); ++ folio_cancel_dirty(folio); + kfree(finfo); +- return; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_all); + } + EXPORT_SYMBOL(netfs_invalidate_folio); + +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index 69975c9c68239..f3e386c69cc8b 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -161,6 +161,10 @@ + EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ + EM(netfs_folio_trace_end_copy, "end-copy") \ + EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ ++ EM(netfs_folio_trace_invalidate_all, "inval-all") \ ++ EM(netfs_folio_trace_invalidate_front, "inval-front") \ ++ EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ ++ EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ + EM(netfs_folio_trace_kill, "kill") \ + EM(netfs_folio_trace_kill_cc, "kill-cc") \ + EM(netfs_folio_trace_kill_g, "kill-g") \ +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch b/queue-6.12/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch new file mode 100644 index 0000000000..663b8bfb4a --- /dev/null +++ b/queue-6.12/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch @@ -0,0 +1,80 @@ +From 3c7a54e19c30d6535d24c81c71cf625eb7f9c377 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:47 +0100 +Subject: netfs: Fix overrun check in netfs_extract_user_iter() + +From: David Howells + +[ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ] + +Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills +pages[], then those pages don't get included in the iterator constructed at +the end of the function. If there was an overfill, memory corruption has +already happened. + +Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into a BVEC iterator") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/iterator.c | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c +index 429e4396e1b00..b375567e0520e 100644 +--- a/fs/netfs/iterator.c ++++ b/fs/netfs/iterator.c +@@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + break; + } + +- if (ret > count) { +- pr_err("get_pages rc=%zd more than %zu\n", ret, count); ++ if (WARN(ret > count, ++ "%s: extract_pages overrun %zd > %zu bytes\n", ++ __func__, ret, count)) { ++ ret = -EIO; + break; + } + +- count -= ret; +- ret += offset; +- cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE); +- +- if (npages + cur_npages > max_pages) { +- pr_err("Out of bvec array capacity (%u vs %u)\n", +- npages + cur_npages, max_pages); ++ cur_npages = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ if (WARN(cur_npages > max_pages - npages, ++ "%s: extract_pages overrun %u > %u pages\n", ++ __func__, npages + cur_npages, max_pages)) { ++ ret = -EIO; + break; + } + ++ count -= ret; ++ ret += offset; ++ + for (i = 0; i < cur_npages; i++) { + len = ret > PAGE_SIZE ? PAGE_SIZE : ret; + bvec_set_page(bv + npages + i, *pages++, len - offset, offset); +@@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + npages += cur_npages; + } + ++ /* Note: Don't try to clean up after EIO. Either we got no pages, so ++ * nothing to clean up, or we got a buffer overrun, memory corruption ++ * and can't trust the stuff in the buffer (a WARN was emitted). ++ */ ++ + if (ret < 0 && (ret == -ENOMEM || npages == 0)) { + for (i = 0; i < npages; i++) + unpin_user_page(bv[i].bv_page); +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-partial-invalidation-of-streaming-write-fo.patch b/queue-6.12/netfs-fix-partial-invalidation-of-streaming-write-fo.patch new file mode 100644 index 0000000000..646b69e209 --- /dev/null +++ b/queue-6.12/netfs-fix-partial-invalidation-of-streaming-write-fo.patch @@ -0,0 +1,49 @@ +From 788c86a29be79a22bf5c252255d70af34f0389b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:57 +0100 +Subject: netfs: Fix partial invalidation of streaming-write folio + +From: David Howells + +[ Upstream commit 6d91acc7fb85d33ea58fca9b964a32a453937f4b ] + +In netfs_invalidate_folio(), if the region of a partial invalidation +overlaps the front (but not all) of a dirty write cached in a streaming +write page (dirty, but not uptodate, with the dirty region tracked by a +netfs_folio struct), the function modifies the dirty region - but +incorrectly as it moves the region forward by setting the start to the +start, not the end, of the invalidation region. + +Fix this by setting finfo->dirty_offset to the end of the invalidation +region (iend). + +Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netfs_inval_folio()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 488a4b1914300..9672904232ad5 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -267,7 +267,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + goto erase_completely; + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; +- finfo->dirty_offset = offset; ++ finfo->dirty_offset = iend; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-potential-deadlock-in-write-through-mode.patch b/queue-6.12/netfs-fix-potential-deadlock-in-write-through-mode.patch new file mode 100644 index 0000000000..35120c99d9 --- /dev/null +++ b/queue-6.12/netfs-fix-potential-deadlock-in-write-through-mode.patch @@ -0,0 +1,119 @@ +From 1a138fd2cf6cf3e2172accee027b634554adfbc0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:51 +0100 +Subject: netfs: Fix potential deadlock in write-through mode + +From: David Howells + +[ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ] + +Fix netfs_advance_writethrough() to always unlock the supplied folio and to +mark it dirty if it isn't yet written to the end. Unfortunately, it can't +be marked for writeback until the folio is done with as that may cause a +deadlock against mmapped reads and writes. + +Even though it has been marked dirty, premature writeback can't occur as +the caller is holding both inode->i_rwsem (which will prevent concurrent +truncation, fallocation, DIO and other writes) and ictx->wb_lock (which +will cause flushing to wait and writeback to skip or wait). + +Note that this may be easier to deal with once the queuing of folios is +split from the generation of subrequests. + +Fixes: 288ace2f57c9 ("netfs: New writeback implementation") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/write_issue.c | 39 +++++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 14 deletions(-) + +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index b7830a15ae40f..2789ac4c80272 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -402,12 +402,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, + if (streamw) + netfs_issue_write(wreq, cache); + +- /* Flip the page to the writeback state and unlock. If we're called +- * from write-through, then the page has already been put into the wb +- * state. +- */ +- if (wreq->origin == NETFS_WRITEBACK) +- folio_start_writeback(folio); ++ folio_start_writeback(folio); + folio_unlock(folio); + + if (fgroup == NETFS_FOLIO_COPY_TO_CACHE) { +@@ -632,29 +627,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c + struct folio *folio, size_t copied, bool to_page_end, + struct folio **writethrough_cache) + { ++ int ret; ++ + _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u", + wreq->debug_id, wreq->iter.count, wreq->wsize, copied, to_page_end); + +- if (!*writethrough_cache) { +- if (folio_test_dirty(folio)) +- /* Sigh. mmap. */ +- folio_clear_dirty_for_io(folio); ++ /* The folio is locked. */ + ++ if (*writethrough_cache != folio) { ++ if (*writethrough_cache) { ++ /* Did the folio get moved? */ ++ folio_put(*writethrough_cache); ++ *writethrough_cache = NULL; ++ } + /* We can make multiple writes to the folio... */ +- folio_start_writeback(folio); + if (wreq->len == 0) + trace_netfs_folio(folio, netfs_folio_trace_wthru); + else + trace_netfs_folio(folio, netfs_folio_trace_wthru_plus); + *writethrough_cache = folio; ++ folio_get(folio); + } + + wreq->len += copied; +- if (!to_page_end) ++ ++ if (!to_page_end) { ++ folio_mark_dirty(folio); ++ folio_unlock(folio); + return 0; ++ } + ++ ret = netfs_write_folio(wreq, wbc, folio); ++ folio_put(*writethrough_cache); + *writethrough_cache = NULL; +- return netfs_write_folio(wreq, wbc, folio); ++ wreq->submitted = wreq->len; ++ return ret; + } + + /* +@@ -668,8 +675,12 @@ int netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_contr + + _enter("R=%x", wreq->debug_id); + +- if (writethrough_cache) ++ if (writethrough_cache) { ++ folio_lock(writethrough_cache); + netfs_write_folio(wreq, wbc, writethrough_cache); ++ folio_put(writethrough_cache); ++ wreq->submitted = wreq->len; ++ } + + netfs_end_issue_write(wreq); + +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-streaming-write-being-overwritten.patch b/queue-6.12/netfs-fix-streaming-write-being-overwritten.patch new file mode 100644 index 0000000000..b2844954f1 --- /dev/null +++ b/queue-6.12/netfs-fix-streaming-write-being-overwritten.patch @@ -0,0 +1,176 @@ +From fe04cc76b7f36dd1b2e6454fdb2b720e921137d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:50 +0100 +Subject: netfs: Fix streaming write being overwritten + +From: David Howells + +[ Upstream commit 7b4dcf1b9455a6e52ac7478b4057dbe10359576d ] + +In order to avoid reading whilst writing, netfslib will allow "streaming +writes" in which dirty data is stored directly into folios without reading +them first. Such folios are marked dirty but may not be marked uptodate. +If a folio is entirely written by a streaming write, uptodate will be set, +otherwise it will have a netfs_folio struct attached to ->private recording +the dirty region. + +In the event that a partially written streaming write page is to be +overwritten entirely by a single write(), netfs_perform_write() will try to +copy over it, but doesn't discard the netfs_folio if it succeeds; further, +it doesn't correctly handle a partial copy that overwrites some of the +dirty data. + +Fix this by the following: + + (1) If the folio is successfully overwritten, free the netfs_folio struct + before marking the page uptodate. + + (2) If the copy to the folio partially fails, but short of the dirty data, + just ignore the copy. + + (3) If the copy partially fails and overwrites some of the dirty data, + accept the copy, update the netfs_folio struct to record the new data. + If the folio is now filled, free the netfs_folio and set uptodate, + otherwise return a partial write. + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0 0x927c0 + write 0x63fb8 0x53c8 0 + copy_range 0xb704 0x19b9 0x24429 0x79380 + write 0x2402b 0x144a2 0x90660 * + write 0x204d5 0x140a0 0x927c0 * + copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * + read 0x00000 0x20000 0x9157c + read 0x20000 0x20000 0x9157c + read 0x40000 0x20000 0x9157c + read 0x60000 0x20000 0x9157c + read 0x7e1a0 0xcfb9 0x9157c + +on cifs with the default cache option. + +It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in +netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- + include/trace/events/netfs.h | 3 +++ + 2 files changed, 37 insertions(+), 13 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 48c66d26e7b7e..19a58aea670cb 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -240,18 +240,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); +- if (unlikely(copied == 0)) ++ if (likely(copied == part)) { ++ if (finfo) { ++ trace = netfs_whole_folio_modify_filled; ++ goto folio_now_filled; ++ } ++ __netfs_set_group(folio, netfs_group); ++ folio_mark_uptodate(folio); ++ trace = netfs_whole_folio_modify; ++ goto copied; ++ } ++ if (copied == 0) + goto copy_failed; +- if (unlikely(copied < part)) { ++ if (!finfo || copied <= finfo->dirty_offset) { + maybe_trouble = true; + iov_iter_revert(iter, copied); + copied = 0; + folio_unlock(folio); + goto retry; + } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; ++ ++ /* We overwrote some existing dirty data, so we have to ++ * accept the partial write. ++ */ ++ finfo->dirty_len += finfo->dirty_offset; ++ if (finfo->dirty_len == flen) { ++ trace = netfs_whole_folio_modify_filled_efault; ++ goto folio_now_filled; ++ } ++ if (copied > finfo->dirty_len) ++ finfo->dirty_len = copied; ++ finfo->dirty_offset = 0; ++ trace = netfs_whole_folio_modify_efault; + goto copied; + } + +@@ -321,16 +341,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto copy_failed; + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); +- folio_mark_uptodate(folio); +- kfree(finfo); + trace = netfs_streaming_cont_filled_page; +- } else { +- trace = netfs_streaming_write_cont; ++ goto folio_now_filled; + } ++ trace = netfs_streaming_write_cont; + goto copied; + } + +@@ -344,6 +358,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + ++ folio_now_filled: ++ if (finfo->netfs_group) ++ folio_change_private(folio, finfo->netfs_group); ++ else ++ folio_detach_private(folio); ++ folio_mark_uptodate(folio); ++ kfree(finfo); + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index f3e386c69cc8b..6395827e83954 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -145,6 +145,9 @@ + EM(netfs_folio_is_uptodate, "mod-uptodate") \ + EM(netfs_just_prefetch, "mod-prefetch") \ + EM(netfs_whole_folio_modify, "mod-whole-f") \ ++ EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ ++ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ ++ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch b/queue-6.12/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch new file mode 100644 index 0000000000..5eeb6f9c09 --- /dev/null +++ b/queue-6.12/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch @@ -0,0 +1,169 @@ +From 7b156b4fb32680bd8505e946baa6d8a0eadbf7ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:44 +0100 +Subject: netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call + +From: Viacheslav Dubeyko + +[ Upstream commit dc7832d05deb4d632e8035e3299e31a3528fa0d0 ] + +The multiple runs of generic/013 test-case is capable +to reproduce a kernel BUG at mm/filemap.c:1504 with +probability of 30%. + +while true; do + sudo ./check generic/013 +done + +[ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 +[ 9849.452412] memcg:ffff8881a1915800 +[ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" +[ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) +[ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 +[ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 +[ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) +[ 9849.452474] ------------[ cut here ]------------ +[ 9849.452476] kernel BUG at mm/filemap.c:1504! +[ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI +[ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) +[ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 +0/2025 +[ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc +cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 +[ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 +[ 9849.504459] PKRU: 55555554 +[ 9849.504626] Call Trace: +[ 9849.505242] +[ 9849.505379] netfs_write_begin+0x7c8/0x10a0 +[ 9849.505877] ? __kasan_check_read+0x11/0x20 +[ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 +[ 9849.507178] ceph_write_begin+0x8c/0x1c0 +[ 9849.507934] generic_perform_write+0x391/0x8f0 +[ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 +[ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 +[ 9849.509581] ? ceph_get_caps+0x63/0xf0 +[ 9849.510259] ? ceph_get_caps+0x63/0xf0 +[ 9849.510530] ceph_write_iter+0xe79/0x1ae0 +[ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 +[ 9849.511839] ? lock_acquire+0x1ad/0x310 +[ 9849.512334] ? ksys_write+0xf9/0x230 +[ 9849.512582] ? lock_is_held_type+0xaa/0x140 +[ 9849.513128] vfs_write+0x512/0x1110 +[ 9849.513634] ? __fget_files+0x33/0x350 +[ 9849.513893] ? __pfx_vfs_write+0x10/0x10 +[ 9849.514143] ? mutex_lock_nested+0x1b/0x30 +[ 9849.514394] ksys_write+0xf9/0x230 +[ 9849.514621] ? __pfx_ksys_write+0x10/0x10 +[ 9849.514887] ? do_syscall_64+0x25e/0x1520 +[ 9849.515122] ? __kasan_check_read+0x11/0x20 +[ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.515655] __x64_sys_write+0x72/0xd0 +[ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 +[ 9849.516130] x64_sys_call+0x22f/0x2390 +[ 9849.516341] do_syscall_64+0x12b/0x1520 +[ 9849.516545] ? do_syscall_64+0x27c/0x1520 +[ 9849.516783] ? do_syscall_64+0x27c/0x1520 +[ 9849.517003] ? lock_release+0x318/0x480 +[ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 +[ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 +[ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.518073] ? do_syscall_64+0x25e/0x1520 +[ 9849.518291] ? __kasan_check_read+0x11/0x20 +[ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.518799] ? do_syscall_64+0x27c/0x1520 +[ 9849.519024] ? local_clock_noinstr+0xf/0x120 +[ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.519544] ? do_syscall_64+0x25e/0x1520 +[ 9849.519781] ? __kasan_check_read+0x11/0x20 +[ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520273] ? do_syscall_64+0x27c/0x1520 +[ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520767] ? irqentry_exit+0x10c/0x6c0 +[ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 +[ 9849.521224] ? exc_page_fault+0xab/0x130 +[ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.521766] RIP: 0033:0x7e36cbd14907 +[ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 +[ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 +[ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 +[ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 +[ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 +[ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 +[ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 +[ 9849.525447] +[ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore +[ 9849.529150] ---[ end trace 0000000000000000 ]--- +[ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 +[ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 +[ 9849.548679] PKRU: 55555554 + +The race sequence: +1. Read completes -> netfs_read_collection() runs +2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...) +3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin() +4. The netfs_unlock_abandoned_read_pages() unlocks the folio +5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO() + +The key reason of the issue that netfs_unlock_abandoned_read_pages() +doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes +folio_unlock() unconditionally. This patch implements in +netfs_unlock_abandoned_read_pages() logic similar to +netfs_unlock_read_folio(). + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Signed-off-by: Viacheslav Dubeyko +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +cc: Ceph Development +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/read_retry.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index 48fb0303f7eee..42abf574e6788 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -249,8 +249,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) + struct folio *folio = folioq_folio(p, slot); + + if (folio && !folioq_is_marked2(p, slot)) { +- trace_netfs_folio(folio, netfs_folio_trace_abandon); +- folio_unlock(folio); ++ if (folio->index == rreq->no_unlock_folio && ++ test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, ++ &rreq->flags)) { ++ _debug("no unlock"); ++ } else { ++ trace_netfs_folio(folio, ++ netfs_folio_trace_abandon); ++ folio_unlock(folio); ++ } + } + } + } +-- +2.53.0 + diff --git a/queue-6.12/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch b/queue-6.12/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch new file mode 100644 index 0000000000..6710c80641 --- /dev/null +++ b/queue-6.12/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch @@ -0,0 +1,80 @@ +From 43476490d29ddda39b158df3c712c1612d942a68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:53 +0100 +Subject: netfs: Fix write streaming disablement if fd open O_RDWR + +From: David Howells + +[ Upstream commit 70a7b9193bbbfceaab5974de66834c64ccc875dd ] + +In netfs_perform_write(), "write streaming" (the caching of dirty data in +dirty but !uptodate folios) is performed to avoid the need to read data +that is just going to get immediately overwritten. However, this is/will +be disabled in three circumstances: if the fd is open O_RDWR, if fscache is +in use (as we need to round out the blocks for DIO) or if content +encryption is enabled (again for rounding out purposes). + +The idea behind disabling it if the fd is open O_RDWR is that we'd need to +flush the write-streaming page before we could read the data, particularly +through mmap. But netfs now fills in the gaps if ->read_folio() is called +on the page, so that is unnecessary. Further, this doesn't actually work +if a separate fd is open for reading. + +Fix this by removing the check for O_RDWR, thereby allowing streaming +writes even when we might read. + +This caused a number of problems with the generic/522 xfstest, but those +are now fixed. + +Fixes: c38f4e96e605 ("netfs: Provide func to copy data to pagecache for buffered write") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 19a58aea670cb..08da4c2512f52 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -197,11 +197,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + /* Decide how we should modify a folio. We might be attempting +- * to do write-streaming, in which case we don't want to a +- * local RMW cycle if we can avoid it. If we're doing local +- * caching or content crypto, we award that priority over +- * avoiding RMW. If the file is open readably, then we also +- * assume that we may want to read what we wrote. ++ * to do write-streaming, as we don't want to a local RMW cycle ++ * if we can avoid it. If we're doing local caching or content ++ * crypto, we award that priority over avoiding RMW. If the ++ * file is open readably, then we let ->read_folio() fill in ++ * the gaps. + */ + finfo = netfs_folio_info(folio); + group = netfs_folio_group(folio); +@@ -277,12 +277,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + + /* We don't want to do a streaming write on a file that loses + * caching service temporarily because the backing store got +- * culled and we don't really want to get a streaming write on +- * a file that's open for reading as ->read_folio() then has to +- * be able to flush it. ++ * culled. + */ +- if ((file->f_mode & FMODE_READ) || +- netfs_is_cache_enabled(ctx)) { ++ if (netfs_is_cache_enabled(ctx)) { + if (finfo) { + netfs_stat(&netfs_n_wh_wstream_conflict); + goto flush_content; +-- +2.53.0 + diff --git a/queue-6.12/netfs-remove-unnecessary-references-to-pages.patch b/queue-6.12/netfs-remove-unnecessary-references-to-pages.patch new file mode 100644 index 0000000000..2a2fc409cf --- /dev/null +++ b/queue-6.12/netfs-remove-unnecessary-references-to-pages.patch @@ -0,0 +1,109 @@ +From b03372bca2e983eed60f77975802bf511d28312b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 5 Oct 2024 19:23:05 +0100 +Subject: netfs: Remove unnecessary references to pages + +From: Matthew Wilcox (Oracle) + +[ Upstream commit e995e8b600260cff3cfaf2607a62be8bdc4aa9c7 ] + +These places should all use folios instead of pages. + +Signed-off-by: Matthew Wilcox (Oracle) +Link: https://lore.kernel.org/r/20241005182307.3190401-4-willy@infradead.org +Signed-off-by: Christian Brauner +Stable-dep-of: ccde2ac757c7 ("netfs: Fix folio->private handling in netfs_perform_write()") +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 8 ++++---- + fs/netfs/buffered_write.c | 14 +++++++------- + 2 files changed, 11 insertions(+), 11 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 1c906035fef02..3b418fed46027 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -631,7 +631,7 @@ static bool netfs_skip_folio_read(struct folio *folio, loff_t pos, size_t len, + if (unlikely(always_fill)) { + if (pos - offset + len <= i_size) + return false; /* Page entirely before EOF */ +- zero_user_segment(&folio->page, 0, plen); ++ folio_zero_segment(folio, 0, plen); + folio_mark_uptodate(folio); + return true; + } +@@ -650,7 +650,7 @@ static bool netfs_skip_folio_read(struct folio *folio, loff_t pos, size_t len, + + return false; + zero_out: +- zero_user_segments(&folio->page, 0, offset, offset + len, plen); ++ folio_zero_segments(folio, 0, offset, offset + len, plen); + return true; + } + +@@ -717,7 +717,7 @@ int netfs_write_begin(struct netfs_inode *ctx, + if (folio_test_uptodate(folio)) + goto have_folio; + +- /* If the page is beyond the EOF, we want to clear it - unless it's ++ /* If the folio is beyond the EOF, we want to clear it - unless it's + * within the cache granule containing the EOF, in which case we need + * to preload the granule. + */ +@@ -777,7 +777,7 @@ int netfs_write_begin(struct netfs_inode *ctx, + EXPORT_SYMBOL(netfs_write_begin); + + /* +- * Preload the data into a page we're proposing to write into. ++ * Preload the data into a folio we're proposing to write into. + */ + int netfs_prefetch_for_write(struct file *file, struct folio *folio, + size_t offset, size_t len) +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index a02bd071cee77..8ba74556bccab 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -85,13 +85,13 @@ static void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode, + * netfs_perform_write - Copy data into the pagecache. + * @iocb: The operation parameters + * @iter: The source buffer +- * @netfs_group: Grouping for dirty pages (eg. ceph snaps). ++ * @netfs_group: Grouping for dirty folios (eg. ceph snaps). + * +- * Copy data into pagecache pages attached to the inode specified by @iocb. ++ * Copy data into pagecache folios attached to the inode specified by @iocb. + * The caller must hold appropriate inode locks. + * +- * Dirty pages are tagged with a netfs_folio struct if they're not up to date +- * to indicate the range modified. Dirty pages may also be tagged with a ++ * Dirty folios are tagged with a netfs_folio struct if they're not up to date ++ * to indicate the range modified. Dirty folios may also be tagged with a + * netfs-specific grouping such that data from an old group gets flushed before + * a new one is started. + */ +@@ -226,11 +226,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + * we try to read it. + */ + if (fpos >= ctx->zero_point) { +- zero_user_segment(&folio->page, 0, offset); ++ folio_zero_segment(folio, 0, offset); + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- zero_user_segment(&folio->page, offset + copied, flen); ++ folio_zero_segment(folio, offset + copied, flen); + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); + trace = netfs_modify_and_clear; +@@ -429,7 +429,7 @@ EXPORT_SYMBOL(netfs_perform_write); + * netfs_buffered_write_iter_locked - write data to a file + * @iocb: IO state structure (file, offset, etc.) + * @from: iov_iter with data to write +- * @netfs_group: Grouping for dirty pages (eg. ceph snaps). ++ * @netfs_group: Grouping for dirty folios (eg. ceph snaps). + * + * This function does all the work needed for actually writing data to a + * file. It does all basic checks, removes SUID from the file, updates +-- +2.53.0 + diff --git a/queue-6.12/nfsd-fix-infinite-loop-in-layout-state-revocation.patch b/queue-6.12/nfsd-fix-infinite-loop-in-layout-state-revocation.patch new file mode 100644 index 0000000000..8b5e107942 --- /dev/null +++ b/queue-6.12/nfsd-fix-infinite-loop-in-layout-state-revocation.patch @@ -0,0 +1,46 @@ +From 13bc52cc7e623e720521777b49ea4e116e32f41b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Apr 2026 14:52:59 -0400 +Subject: NFSD: Fix infinite loop in layout state revocation + +From: Chuck Lever + +[ Upstream commit 4f8ef58c10bfe5f86a643c7c8331b37e69e3dae1 ] + +find_one_sb_stid() skips stids whose sc_status is non-zero, but the +SC_TYPE_LAYOUT case in nfsd4_revoke_states() never sets sc_status +before calling nfsd4_close_layout(). The retry loop therefore finds +the same layout stid on every iteration, hanging the revoker +indefinitely. + +Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") +Reported-by: Dai Ngo +Reviewed-by: Jeff Layton +Tested-by: Dai Ngo +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 1a15e458b178a..2d91747297820 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -1832,6 +1832,13 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) + break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(stid); ++ spin_lock(&clp->cl_lock); ++ if (stid->sc_status == 0) { ++ stid->sc_status |= ++ SC_STATUS_ADMIN_REVOKED; ++ atomic_inc(&clp->cl_admin_revoked); ++ } ++ spin_unlock(&clp->cl_lock); + nfsd4_close_layout(ls); + break; + } +-- +2.53.0 + diff --git a/queue-6.12/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch b/queue-6.12/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch new file mode 100644 index 0000000000..7d14672d3a --- /dev/null +++ b/queue-6.12/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch @@ -0,0 +1,43 @@ +From 5268fbaa3bbf22af7f356dfe2bffbe994291228d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 19:23:01 +0800 +Subject: nsfs: fix wrong error code returned for pidns ioctls + +From: Zhihao Cheng + +[ Upstream commit 725ecd80688bf3c57ca9205431f2c06174ff0756 ] + +When executing NS_GET_PID_FROM_PIDNS (or similar pidns ioctls), if the +target task cannot be found in the corresponding pid_ns, the error code +should be ESRCH instead of ENOTTY. + +This bug was introduced when the extensible ioctl handling was added. +Without proper return, ret would be overwritten by the default case in +the extensible ioctl switch statement. + +Fixes: a1d220d9dafa8 ("nsfs: iterate through mount namespaces") +Signed-off-by: Zhihao Cheng +Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com +Reviewed-by: Yang Erkun +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/nsfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/nsfs.c b/fs/nsfs.c +index 0f4b0fed9265f..eb232f5292f8f 100644 +--- a/fs/nsfs.c ++++ b/fs/nsfs.c +@@ -235,7 +235,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl, + else + tsk = find_task_by_pid_ns(arg, pid_ns); + if (!tsk) +- break; ++ return ret; + + switch (ioctl) { + case NS_GET_PID_FROM_PIDNS: +-- +2.53.0 + diff --git a/queue-6.12/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch b/queue-6.12/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch new file mode 100644 index 0000000000..7a86e72227 --- /dev/null +++ b/queue-6.12/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch @@ -0,0 +1,42 @@ +From 05d6234907ab5fae847bd2ede171982efb6f6261 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 10:00:36 +0530 +Subject: octeontx2-af: npc: Fix allmulticast skip logic for LBK and SDP VFs + +From: Ratheesh Kannoth + +[ Upstream commit 9eddc819f00b5b74bb4ac91396f80bd35f5f3561 ] + +When installing the allmulticast NPC rule, rvu_npc_install_allmulti_entry() +should skip LBK and SDP VFs (only CGX PF/VF may add the entry). The +code combined is_lbk_vf() and is_sdp_vf() with logical AND, which is +never true for a single pcifunc, so the intended early return never ran. + +Use logical OR instead. + +Cc: Geetha sowjanya +Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks") +Signed-off-by: Ratheesh Kannoth +Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index a78923d7811dc..e3038a912a580 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -853,7 +853,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, + u16 vf_func; + + /* Only CGX PF/VF can add allmulticast entry */ +- if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc)) ++ if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc)) + return; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); +-- +2.53.0 + diff --git a/queue-6.12/pds_core-ensure-null-termination-for-firmware-versio.patch b/queue-6.12/pds_core-ensure-null-termination-for-firmware-versio.patch new file mode 100644 index 0000000000..fe128bb22b --- /dev/null +++ b/queue-6.12/pds_core-ensure-null-termination-for-firmware-versio.patch @@ -0,0 +1,47 @@ +From 136c2fa5b43f109f0923cc4ef1da233dc3c91616 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:58:42 +0000 +Subject: pds_core: ensure null-termination for firmware version strings + +From: Nikhil P. Rao + +[ Upstream commit 3d4432d34c1992701289cbe12df9fd024f315998 ] + +The driver passes fw_version directly to devlink_info_version_stored_put() +without ensuring null-termination. While current firmware null-terminates +these strings, the driver should not rely on this behavior. Add explicit +null-termination to prevent potential issues if firmware behavior changes. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c +index d8dc39da4161f..621791a3c543b 100644 +--- a/drivers/net/ethernet/amd/pds_core/devlink.c ++++ b/drivers/net/ethernet/amd/pds_core/devlink.c +@@ -121,12 +121,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + + listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); + for (i = 0; i < listlen; i++) { ++ char *fw_ver = fw_list.fw_names[i].fw_version; ++ + if (i < ARRAY_SIZE(fw_slotnames)) + strscpy(buf, fw_slotnames[i], sizeof(buf)); + else + snprintf(buf, sizeof(buf), "fw.slot_%d", i); +- err = devlink_info_version_stored_put(req, buf, +- fw_list.fw_names[i].fw_version); ++ fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0'; ++ err = devlink_info_version_stored_put(req, buf, fw_ver); + if (err) + return err; + } +-- +2.53.0 + diff --git a/queue-6.12/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch b/queue-6.12/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch new file mode 100644 index 0000000000..2480b8aaf4 --- /dev/null +++ b/queue-6.12/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch @@ -0,0 +1,50 @@ +From ac8596435f22812be00ce4a43ca6eaf0e1404e0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:07 +0000 +Subject: pds_core: fix debugfs_lookup dentry leak and error handling + +From: Nikhil P. Rao + +[ Upstream commit dc416e32baaeb620b9809e9e25fc7b30889686e9 ] + +debugfs_lookup() returns a dentry with an elevated reference count that +must be released with dput(). The current code discards the returned +dentry without calling dput(), causing a reference leak on every +firmware reset recovery. + +Additionally, when CONFIG_DEBUG_FS is disabled, debugfs_lookup() +returns ERR_PTR(-ENODEV), not NULL. The current check passes for error +pointers and would call dput() on an invalid pointer, causing a crash. + +Fixes: bc90fbe0c318 ("pds_core: Rework teardown/setup flow to be more common") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/debugfs.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c +index 04c5e3abd8d70..810a0cd9bcac8 100644 +--- a/drivers/net/ethernet/amd/pds_core/debugfs.c ++++ b/drivers/net/ethernet/amd/pds_core/debugfs.c +@@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity); + + void pdsc_debugfs_add_ident(struct pdsc *pdsc) + { ++ struct dentry *dentry; ++ + /* This file will already exist in the reset flow */ +- if (debugfs_lookup("identity", pdsc->dentry)) ++ dentry = debugfs_lookup("identity", pdsc->dentry); ++ if (!IS_ERR_OR_NULL(dentry)) { ++ dput(dentry); + return; ++ } + + debugfs_create_file("identity", 0400, pdsc->dentry, + pdsc, &identity_fops); +-- +2.53.0 + diff --git a/queue-6.12/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch b/queue-6.12/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch new file mode 100644 index 0000000000..f6b7d1012f --- /dev/null +++ b/queue-6.12/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch @@ -0,0 +1,62 @@ +From c3eed30b3b2e82abbdc4616fed647b5ff6711f59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:05 +0000 +Subject: pds_core: fix error handling in pdsc_devcmd_wait + +From: Nikhil P. Rao + +[ Upstream commit 0e46b6635b03d29807f810c3b415c4755a3f958d ] + +Fix two cases where pdsc_devcmd_wait() returns stale success from +the completion register instead of an error: + +1. FW crash: If firmware stops running, the wait loop breaks early with + running=false. The condition "if ((!done || timeout) && running)" is + false, so error handling is bypassed and stale status is returned. + Check !running first and return -ENXIO. + +2. Timeout: If a command times out, err is set to -ETIMEDOUT but then + overwritten by pdsc_err_to_errno(status) which reads stale status. + Return -ETIMEDOUT immediately after cleaning up. + +Both errors now propagate to pdsc_devcmd_locked() which queues +health_work for recovery. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/dev.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c +index 495ef4ef8c103..1d1e559bd99d3 100644 +--- a/drivers/net/ethernet/amd/pds_core/dev.c ++++ b/drivers/net/ethernet/amd/pds_core/dev.c +@@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds) + dev_dbg(dev, "DEVCMD %d %s after %ld secs\n", + opcode, pdsc_devcmd_str(opcode), duration / HZ); + +- if ((!done || timeout) && running) { ++ if (!running) { ++ dev_err(dev, "DEVCMD %d %s fw not running\n", ++ opcode, pdsc_devcmd_str(opcode)); ++ pdsc_devcmd_clean(pdsc); ++ return -ENXIO; ++ } ++ ++ if (!done || timeout) { + dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n", + opcode, pdsc_devcmd_str(opcode), done, timeout, + max_seconds); +- err = -ETIMEDOUT; + pdsc_devcmd_clean(pdsc); ++ return -ETIMEDOUT; + } + + status = pdsc_devcmd_status(pdsc); +-- +2.53.0 + diff --git a/queue-6.12/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-6.12/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..8d52e88b52 --- /dev/null +++ b/queue-6.12/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From a4e2cc2792fc77b9521569659eda12a190bf683c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 04f4fb4bed702..f882bc57649c7 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-6.12/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch b/queue-6.12/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch new file mode 100644 index 0000000000..8e8ca158e9 --- /dev/null +++ b/queue-6.12/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch @@ -0,0 +1,56 @@ +From 27e2463eff4e1fe6221736ca40b40204bcc025d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:44:58 +0530 +Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for + sm8150 + +From: Maulik Shah + +[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not +support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC +pin and already reflected in the wake irq map. + +Remove the unsupported wakeup interrupts from the map. + +Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") +Reviewed-by: Konrad Dybcio +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c +index f8f5bee74f1dc..565aab84835cb 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c ++++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c +@@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, + { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, +- { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, +- { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, ++ { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, ++ { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, + { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, + { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, +- { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, ++ { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, + { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, + { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, + { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, +- { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, ++ { 144, 115 }, { 147, 102 }, { 150, 107 }, + { 152, 108 }, { 153, 109 } + }; + +-- +2.53.0 + diff --git a/queue-6.12/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch b/queue-6.12/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch new file mode 100644 index 0000000000..8396418526 --- /dev/null +++ b/queue-6.12/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch @@ -0,0 +1,49 @@ +From 9157805f9eca468f5577bde0fed163dffd366779 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 09:05:45 +0000 +Subject: pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high + pins during suspend/resume +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Biju Das + +[ Upstream commit 6dba9b7268cc50166bce47608670192fd874e363 ] + +When saving/restoring pull-up/down register state during suspend/resume, +the second PUPD register access was incorrectly using the same base offset +as the first, effectively reading/writing the same register twice instead +of the adjacent one. + +Add the correct + 4 byte offset to the second RZG2L_PCTRL_REG_ACCESS32 +call so that pupd[1][port] is properly saved and restored from the next +32-bit register in the PUPD register pair, covering pins 4–7 of ports +with 4 or more pins. + +Fixes: b2bd65fbb617 ("pinctrl: renesas: rzg2l: Add suspend/resume support for pull up/down") +Signed-off-by: Biju Das +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +index bcb0c39369e05..17e27879fd623 100644 +--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c ++++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +@@ -2849,7 +2849,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), + cache->pupd[0][port]); + if (pincnt >= 4) { +- RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off) + 4, + cache->pupd[1][port]); + } + } +-- +2.53.0 + diff --git a/queue-6.12/platform-surface-aggregator_registry-omit-battery-ac.patch b/queue-6.12/platform-surface-aggregator_registry-omit-battery-ac.patch new file mode 100644 index 0000000000..64a773f988 --- /dev/null +++ b/queue-6.12/platform-surface-aggregator_registry-omit-battery-ac.patch @@ -0,0 +1,47 @@ +From 5f7655a988b44e8130761f2588a4a418d3de1ef7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Apr 2026 15:43:47 +1200 +Subject: platform/surface: aggregator_registry: omit battery & AC nodes on + Surface Laptop 7 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Oliver White + +[ Upstream commit 0488073a6c84571dd3cffe581a4a73a5fceb099d ] + +Surface Laptop 7 exposes battery and AC status via Qualcomm PMIC GLINK +qcom_battmgr. Registering the standard SSAM battery and AC client +devices on this platform causes duplicate power-supply devices to +appear. + +Drop the SSAM battery and AC nodes from the Surface Laptop 7 registry +group so that only the qcom_battmgr power supplies are instantiated. + +Fixes: b27622f13172 ("platform/surface: Add OF support") +Signed-off-by: Oliver White +Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/surface/surface_aggregator_registry.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c +index 25c8aa2131d63..9826feb9c2825 100644 +--- a/drivers/platform/surface/surface_aggregator_registry.c ++++ b/drivers/platform/surface/surface_aggregator_registry.c +@@ -295,8 +295,6 @@ static const struct software_node *ssam_node_group_sl6[] = { + /* Devices for Surface Laptop 7. */ + static const struct software_node *ssam_node_group_sl7[] = { + &ssam_node_root, +- &ssam_node_bat_ac, +- &ssam_node_bat_main, + &ssam_node_tmp_perf_profile_with_fan, + &ssam_node_fan_speed, + &ssam_node_hid_sam_keyboard, +-- +2.53.0 + diff --git a/queue-6.12/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-6.12/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..279b8f74bb --- /dev/null +++ b/queue-6.12/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From 8fe2c8784082acf6d1e1d0e4a41a3b927f954e67 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 6b23ba78e028f..f7bc252650d5a 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-6.12/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-6.12/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..46662fd845 --- /dev/null +++ b/queue-6.12/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From a53856014437af7328719e9c9a65fece5a1826a1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 52535576772ad..aeedc77bed7fc 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-6.12/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-6.12/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..20912ec8de --- /dev/null +++ b/queue-6.12/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From 54922c12424aafe145d87650a95cef382e2cc7e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index 0e81904548f36..7d0c5b986b259 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -673,12 +673,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-6.12/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-6.12/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..8478f0d875 --- /dev/null +++ b/queue-6.12/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From a4541eb2c0f96da38415aaa90188e74680028daf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index a353e830b65fd..d64aac2895f37 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -275,12 +275,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-6.12/powerpc-fix-dead-default-for-guest_state_buffer_test.patch b/queue-6.12/powerpc-fix-dead-default-for-guest_state_buffer_test.patch new file mode 100644 index 0000000000..a4adbdad6a --- /dev/null +++ b/queue-6.12/powerpc-fix-dead-default-for-guest_state_buffer_test.patch @@ -0,0 +1,54 @@ +From 84dbc19e0c13e3b6da51cb25c57e0a53b3adc1d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Apr 2026 17:15:45 +0100 +Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST + +From: Julian Braha + +[ Upstream commit aef656a0e6c01796190bb5bd2bdba1c644ed7811 ] + +The GUEST_STATE_BUFFER_TEST config option should default +to KUNIT_ALL_TESTS so that if all tests are enabled then +it is included, but currently the 'default KUNIT_ALL_TESTS' +statement is shadowed by 'def_tristate n', +meaning that this second default statement is currently dead code. + +It looks to me like the commit +6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +intended to set the default to KUNIT_ALL_TESTS, but mistakenly +missed the def_tristate. + +This dead code was found by kconfirm, a static analysis tool for Kconfig. + +Fixes: 6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +Signed-off-by: Julian Braha +Tested-by: Gautam Menghani +Reviewed-by: Amit Machhiwal +Reviewed-by: Harsh Prateek Bora +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/Kconfig.debug | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug +index 0bbec4afc0d59..1e2b51280e602 100644 +--- a/arch/powerpc/Kconfig.debug ++++ b/arch/powerpc/Kconfig.debug +@@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST + depends on DEBUG_KERNEL + + config GUEST_STATE_BUFFER_TEST +- def_tristate n ++ def_tristate KUNIT_ALL_TESTS + prompt "Enable Guest State Buffer unit tests" + depends on KUNIT + depends on KVM_BOOK3S_HV_POSSIBLE +- default KUNIT_ALL_TESTS + help + The Guest State Buffer is a data format specified in the PAPR. + It is by hcalls to communicate the state of L2 guests between +-- +2.53.0 + diff --git a/queue-6.12/powerpc-time-remove-redundant-preempt_disable-enable.patch b/queue-6.12/powerpc-time-remove-redundant-preempt_disable-enable.patch new file mode 100644 index 0000000000..57beb8af0e --- /dev/null +++ b/queue-6.12/powerpc-time-remove-redundant-preempt_disable-enable.patch @@ -0,0 +1,98 @@ +From f7efe3b3424de332d09a7485735d25b2d3ac08df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 13:44:13 +0530 +Subject: powerpc/time: Remove redundant preempt_disable|enable() calls from + arch_irq_work_raise() + +From: Sayali Patil + +[ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] + +A kernel panic is observed when handling machine check exceptions from +real mode. + + BUG: Unable to handle kernel data access on read at 0xc00000006be21300 + Oops: Kernel access of bad area, sig: 11 [#1] + MSR: 8000000000001003 CR: 88222248 XER: 00000005 + CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 + NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 + LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 + Call Trace: + [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 + [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 + +The crash occurs because arch_irq_work_raise() calls preempt_disable() +from machine check exception (MCE) handlers running in real mode. In +this context, accessing the preempt_count can fault, leading to the panic. + +The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix +oops due to perf_event_do_pending call") to avoid races while raising +irq work from exception context. + +Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when +queueing work on the local CPU") added preemption protection in +irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per +cpu atomics instead of regular atomics") added equivalent +protection in irq_work_queue_on() before reaching arch_irq_work_raise(): + + irq_work_queue() / irq_work_queue_on() + -> preempt_disable() + -> __irq_work_queue_local() + -> irq_work_raise() + -> arch_irq_work_raise() + +As a result, callers other than mce_irq_work_raise() already execute +with preemption disabled, making the additional +preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +redundant. + +The arch_irq_work_raise() function executes in NMI context when called +from MCE handler. Hence we will not be preempted or scheduled out since +we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove +the preempt_disable()/preempt_enable() calls from here. + +Remove it to avoid accessing preempt_count from real mode context. + +Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") +Suggested-by: Mahesh Salgaonkar +Acked-by: Shrikanth Hegde +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Sayali Patil +[Maddy: Fixed the commit title] +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/kernel/time.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c +index 0ff9f038e800d..ce7f91172ec2b 100644 +--- a/arch/powerpc/kernel/time.c ++++ b/arch/powerpc/kernel/time.c +@@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); + + #endif /* 32 vs 64 bit */ + ++/* ++ * Must be called with preemption disabled since it updates ++ * per-CPU irq_work state and programs the local CPU decrementer. ++ */ + void arch_irq_work_raise(void) + { + /* +@@ -471,10 +475,8 @@ void arch_irq_work_raise(void) + * which could get tangled up if we're messing with the same state + * here. + */ +- preempt_disable(); + set_irq_work_pending_flag(); + set_dec(1); +- preempt_enable(); + } + + static void set_dec_or_work(u64 val) +-- +2.53.0 + diff --git a/queue-6.12/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-6.12/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..83a33842ce --- /dev/null +++ b/queue-6.12/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From 5801ce9d9bac9cdd7901946b8950b1c99ad710f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 3f305e694fe8c..1b1c6ea4ee5a4 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.12/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch b/queue-6.12/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch new file mode 100644 index 0000000000..e0d9fcb912 --- /dev/null +++ b/queue-6.12/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch @@ -0,0 +1,87 @@ +From 04467441ed85a2e305fd04fbadc9c0076fc3705d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 25 Jan 2026 00:52:12 -0500 +Subject: riscv: mm: Fixup no5lvl failure when vaddr is invalid +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guo Ren (Alibaba DAMO Academy) + +[ Upstream commit db909bd7986c10da074917af3dae83a60fa65093 ] + +Unlike no4lvl, no5lvl still continues to detect satp, which +requires va=pa mapping. When pa=0x800000000000, no5lvl +would fail in Sv48 mode due to an illegal VA value of +0x800000000000. + +So, prevent detecting the satp flow for no5lvl, when +vaddr is invalid. Add the is_vaddr_valid() function for +checking. + +Fixes: 26e7aacb83df ("riscv: Allow to downgrade paging mode from the command line") +Cc: Alexandre Ghiti +Cc: Björn Töpel +Signed-off-by: Guo Ren (Alibaba DAMO Academy) +Tested-by: Fangyu Yu +Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org +[pjw@kernel.org: cleaned up commit message] +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index 8d167e09f1fea..8cd8bc9b82cb2 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -807,6 +807,27 @@ static void __init set_mmap_rnd_bits_max(void) + mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3; + } + ++static bool __init is_vaddr_valid(unsigned long va) ++{ ++ unsigned long up = 0; ++ ++ switch (satp_mode) { ++ case SATP_MODE_39: ++ up = 1UL << 38; ++ break; ++ case SATP_MODE_48: ++ up = 1UL << 47; ++ break; ++ case SATP_MODE_57: ++ up = 1UL << 56; ++ break; ++ default: ++ return false; ++ } ++ ++ return (va < up) || (va >= (ULONG_MAX - up + 1)); ++} ++ + /* + * There is a simple way to determine if 4-level is supported by the + * underlying hardware: establish 1:1 mapping in 4-level page table mode +@@ -842,6 +863,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + set_satp_mode_pmd + PMD_SIZE, + PMD_SIZE, PAGE_KERNEL_EXEC); + retry: ++ if (!is_vaddr_valid(set_satp_mode_pmd)) ++ goto out; ++ + create_pgd_mapping(early_pg_dir, + set_satp_mode_pmd, + pgtable_l5_enabled ? +@@ -864,6 +888,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + disable_pgtable_l4(); + } + ++out: + memset(early_pg_dir, 0, PAGE_SIZE); + memset(early_p4d, 0, PAGE_SIZE); + memset(early_pud, 0, PAGE_SIZE); +-- +2.53.0 + diff --git a/queue-6.12/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch b/queue-6.12/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch new file mode 100644 index 0000000000..952993534a --- /dev/null +++ b/queue-6.12/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch @@ -0,0 +1,41 @@ +From 4c827983d88e597d40fb2d5fa25dd7abf31622a1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 12:53:17 -0500 +Subject: scsi: sd: Fix return code handling in sd_spinup_disk() + +From: Mike Christie + +[ Upstream commit 6ea68a8dc7d2711504d944811981a5304af7d7a9 ] + +As found by smatch-ci, scsi_execute_cmd() can return negative or positve +values so we should use a int instead of unsigned int. + +Fixes: b4d0c33a32c3 ("scsi: sd: Fix sshdr use in sd_spinup_disk") +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u +Signed-off-by: Mike Christie +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/sd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c +index f37f031971dfd..ab5cf8460aca5 100644 +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -2392,8 +2392,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) + { + static const u8 cmd[10] = { TEST_UNIT_READY }; + unsigned long spintime_expire = 0; +- int spintime, sense_valid = 0; +- unsigned int the_result; ++ int the_result, spintime, sense_valid = 0; + struct scsi_sense_hdr sshdr; + struct scsi_failure failure_defs[] = { + /* Do not retry Medium Not Present */ +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 4feda3f674..ba826063ee 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -139,3 +139,134 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +arm-dts-renesas-genmai-drop-superfluous-cells.patch +arm-dts-renesas-rskrza1-drop-superfluous-cells.patch +pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch +hid-uclogic-fix-regression-of-input-name-assignment.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch +firmware-arm_ffa-refactor-addition-of-partition-info.patch +firmware-arm_ffa-unregister-the-ff-a-devices-when-cl.patch +firmware-arm_ffa-remove-unnecessary-declaration-of-f.patch +firmware-arm_ffa-allow-multiple-uuids-per-partition-.patch +firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch +riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch +firmware-arm_ffa-fix-big-endian-support-in-__ffa_par.patch +firmware-arm_ffa-bound-partition_info_get_regs-copie.patch +firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch +firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch +arm-integrator-fix-early-initialization.patch +alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch +alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch +btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-make-legacy-configs-user-selectable.patch +netfilter-exclude-legacy-tables-on-preempt_rt.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-x_tables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch +test_kprobes-clear-kprobes-between-test-runs.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +ice-fix-setting-rss-vsi-hash-for-e830.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +net-lan966x-avoid-unregistering-netdev-on-register-f.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +nfsd-fix-infinite-loop-in-layout-state-revocation.patch +irqchip-ath79-cpu-remove-unused-function.patch +ublk-reject-max_sectors-smaller-than-page_sectors-in.patch +nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch +irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch +zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch +tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch +powerpc-fix-dead-default-for-guest_state_buffer_test.patch +netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch +netfs-fix-overrun-check-in-netfs_extract_user_iter.patch +netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch +netfs-defer-the-emission-of-trace_netfs_folio.patch +netfs-fix-streaming-write-being-overwritten.patch +netfs-fix-potential-deadlock-in-write-through-mode.patch +netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch +netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch +netfs-fix-partial-invalidation-of-streaming-write-fo.patch +netfs-fix-a-few-minor-bugs-in-netfs_page_mkwrite.patch +netfs-remove-unnecessary-references-to-pages.patch +netfs-fix-folio-private-handling-in-netfs_perform_wr.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch +wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch +blk-integrity-remove-seed-for-user-mapped-buffers.patch +block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch +block-recompute-nr_integrity_segments-in-blk_insert_.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +block-modify-bio_integrity_map_user-to-accept-iov_it.patch +block-drop-direction-param-from-bio_integrity_copy_u.patch +blk-integrity-use-simpler-alignment-check.patch +blk-integrity-enable-p2p-source-and-destination.patch +block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch +accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch +net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +powerpc-time-remove-redundant-preempt_disable-enable.patch +net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +net-phy-dp83tc811-add-reading-of-abilities.patch +x86-xen-fix-xen_e820_swap_entry_with_ram.patch +tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch +net-mlx5-do-not-restore-destination-less-tc-rules.patch +scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch +alsa-scarlett2-add-missing-error-check-when-initiali.patch +io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch +btrfs-fix-squota-accounting-during-enable-generation.patch +spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch +drm-xe-vf-fix-signature-of-print-functions.patch +drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch +wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch +ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch +drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch +kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-surface-aggregator_registry-omit-battery-ac.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +net-bridge-flush-multicast-groups-when-snooping-is-d.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch +pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch +wifi-mac80211-fix-mle-defragmentation.patch +alsa-seq-serialize-ump-output-teardown-with-event_in.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch +net-ag71xx-check-error-for-platform_get_irq.patch +bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch +gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch +drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch +ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch +octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch +pds_core-ensure-null-termination-for-firmware-versio.patch +net-gro-don-t-merge-zcopy-skbs.patch +loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch diff --git a/queue-6.12/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch b/queue-6.12/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch new file mode 100644 index 0000000000..69243c63c0 --- /dev/null +++ b/queue-6.12/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch @@ -0,0 +1,38 @@ +From b7a4ba67ed979e5a73ddbc838bed1fa0290a7764 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 01:55:37 +0800 +Subject: spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() + +From: Felix Gu + +[ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] + +When DMA read times out in mtk_snand_read_page_cache(), the original code +erroneously jumped to cleanup label which skips DMA unmapping and ECC +disable, causing a resource leak. + +Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") +Signed-off-by: Felix Gu +Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-mtk-snfi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c +index 8234064921f36..20260f577f056 100644 +--- a/drivers/spi/spi-mtk-snfi.c ++++ b/drivers/spi/spi-mtk-snfi.c +@@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; +- goto cleanup; ++ goto cleanup2; + } + + // Wait for BUS_SEC_CNTR returning expected value +-- +2.53.0 + diff --git a/queue-6.12/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-6.12/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..361e87ae35 --- /dev/null +++ b/queue-6.12/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From a93fdad90ba3581b725dcf880e5e38a9c051056b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index bf4e5f49030b7..dd39cabb39001 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1172,7 +1172,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-6.12/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch b/queue-6.12/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch new file mode 100644 index 0000000000..1268a376c8 --- /dev/null +++ b/queue-6.12/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch @@ -0,0 +1,52 @@ +From 2587ce39cb2c106a7f5176aaf6e90bee96024a0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 12:08:46 +0000 +Subject: tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key(). + +From: Kuniyuki Iwashima + +[ Upstream commit 03cb001ef87b3f8d859cf7f96329acf3d6235d29 ] + +lockdep_sock_is_held() was added in tcp_ao_established_key() +by the cited commit. + +It can be called from tcp_v[46]_timewait_ack() with twsk. + +Since it does not have sk->sk_lock, the lockdep annotation +results in out-of-bound access. + + $ pahole -C tcp_timewait_sock vmlinux | grep size + /* size: 288, cachelines: 5, members: 8 */ + $ pahole -C sock vmlinux | grep sk_lock + socket_lock_t sk_lock; /* 440 192 */ + +Let's not use lockdep_sock_is_held() for TCP_TIME_WAIT. + +Fixes: 6b2d11e2d8fc ("net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/tcp_ao.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c +index 72957523c2eca..be38712265c37 100644 +--- a/net/ipv4/tcp_ao.c ++++ b/net/ipv4/tcp_ao.c +@@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, + { + struct tcp_ao_key *key; + +- hlist_for_each_entry_rcu(key, &ao->head, node, lockdep_sock_is_held(sk)) { ++ hlist_for_each_entry_rcu(key, &ao->head, node, ++ sk_fullsock(sk) && lockdep_sock_is_held(sk)) { + if ((sndid >= 0 && key->sndid != sndid) || + (rcvid >= 0 && key->rcvid != rcvid)) + continue; +-- +2.53.0 + diff --git a/queue-6.12/test_kprobes-clear-kprobes-between-test-runs.patch b/queue-6.12/test_kprobes-clear-kprobes-between-test-runs.patch new file mode 100644 index 0000000000..1c5550ee7f --- /dev/null +++ b/queue-6.12/test_kprobes-clear-kprobes-between-test-runs.patch @@ -0,0 +1,128 @@ +From 69d6e4180e79a37109bf13b53270649b40a3a4dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: test_kprobes: clear kprobes between test runs + +From: Martin Kaiser + +[ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ] + +Running the kprobes sanity tests twice makes all tests fail and +eventually crashes the kernel. + +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # Totals: pass:5 fail:0 skip:0 total:5 + ok 1 kprobes_test +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 + Expected 0 == register_kprobe(&kp), but + register_kprobe(&kp) == -22 (0xffffffffffffffea) +... + Unable to handle kernel paging request ... + +The testsuite defines several kprobes and kretprobes as static variables +that are preserved across test runs. + +After register_kprobe and unregister_kprobe, a kprobe contains some +leftover data that must be cleared before the kprobe can be registered +again. The tests are setting symbol_name to define the probe location. +Address and flags must be cleared. + +The existing code clears some of the probes between subsequent tests, but +not between two test runs. The leftover data from a previous test run +makes the registrations fail in the next run. + +Move the cleanups for all kprobes into kprobes_test_init, this function +is called before each single test (including the first test of a test +run). + +Link: https://lore.kernel.org/all/20260507134615.1010905-1-martin@kaiser.cx/ + +Fixes: e44e81c5b90f ("kprobes: convert tests to kunit") +Signed-off-by: Martin Kaiser +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + lib/test_kprobes.c | 29 ++++++++++++++++++----------- + 1 file changed, 18 insertions(+), 11 deletions(-) + +diff --git a/lib/test_kprobes.c b/lib/test_kprobes.c +index b7582010125c3..06e729e4de051 100644 +--- a/lib/test_kprobes.c ++++ b/lib/test_kprobes.c +@@ -12,6 +12,12 @@ + + #define div_factor 3 + ++#define KP_CLEAR(_kp) \ ++do { \ ++ (_kp).addr = NULL; \ ++ (_kp).flags = 0; \ ++} while (0) ++ + static u32 rand1, preh_val, posth_val; + static u32 (*target)(u32 value); + static u32 (*recursed_target)(u32 value); +@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) + + current_test = test; + +- /* addr and flags should be cleard for reusing kprobe. */ +- kp.addr = NULL; +- kp.flags = 0; +- + KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); + preh_val = 0; + posth_val = 0; +@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) + struct kretprobe *rps[2] = {&rp, &rp2}; + + current_test = test; +- /* addr and flags should be cleard for reusing kprobe. */ +- rp.kp.addr = NULL; +- rp.kp.flags = 0; + KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); + + krph_val = 0; +@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) + unsigned long myretaddr = (unsigned long)__builtin_return_address(0); + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + /* + * Run the stacktrace_driver() to record correct return address in +@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + struct kretprobe *rps[2] = {&rp3, &rp4}; + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); + +@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + + static int kprobes_test_init(struct kunit *test) + { ++ KP_CLEAR(kp); ++ KP_CLEAR(kp2); ++ KP_CLEAR(kp_missed); ++#ifdef CONFIG_KRETPROBES ++ KP_CLEAR(rp.kp); ++ KP_CLEAR(rp2.kp); ++#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE ++ KP_CLEAR(rp3.kp); ++ KP_CLEAR(rp4.kp); ++#endif ++#endif ++ + target = kprobe_target; + target2 = kprobe_target2; + recursed_target = kprobe_recursed_target; +-- +2.53.0 + diff --git a/queue-6.12/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch b/queue-6.12/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch new file mode 100644 index 0000000000..d1a02b88fa --- /dev/null +++ b/queue-6.12/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch @@ -0,0 +1,125 @@ +From 0f42f8496a371af0bf67ac2eaa784c6cf2190bb3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 08:58:25 -0400 +Subject: tls: Preserve sk_err across recvmsg() when data has been copied + +From: Chuck Lever + +[ Upstream commit f508262ae9f21fe0e6c0749948b9dc7dd5a62a70 ] + +The sk_err check in tls_rx_rec_wait() consumes the error via +sock_error(), which clears sk_err atomically. When the caller +(tls_sw_recvmsg, tls_sw_splice_read, or tls_sw_read_sock) already +has bytes copied to userspace, it returns those bytes and discards +the error from this call. sk_err is now zero on the socket, so the +next read syscall observes only RCV_SHUTDOWN and reports a clean +EOF instead of the actual error (typically -ECONNRESET). + +The race is reachable when tls_read_flush_backlog()'s periodic +sk_flush_backlog() triggers tcp_reset() in the middle of a +multi-record read. + +Pass a has_copied flag to tls_rx_rec_wait(). When has_copied is +false, consume sk_err via sock_error() as before. When has_copied +is true, report the error from READ_ONCE() but leave sk_err set: +the caller returns the byte count and discards the err from this +call, and the next read syscall surfaces the preserved sk_err. This +mirrors the tcp_recvmsg() preserve-and-surface pattern. + +The decrypt-abort path is unaffected: tls_err_abort() raises +sk_err to EBADMSG after tls_rx_rec_wait() returns, and nothing +on the caller's return path consumes it, so the EBADMSG surfaces +on the next read. + +tls_sw_splice_read() passes has_copied=false: it processes +one record per call, so no bytes have been copied within the +function when tls_rx_rec_wait() runs. A reset that arrives +between iterations of splice_direct_to_actor() (the sendfile() +path) is still consumed by sock_error() in the later call, and the +outer loop returns the prior iterations' byte count and drops the +error. tcp_splice_read() exhibits the same pattern at the iteration +boundary; addressing it belongs at the splice_direct_to_actor() +layer and is out of scope here. + +Fixes: c46b01839f7a ("tls: rx: periodically flush socket backlog") +Suggested-by: Jakub Kicinski +Signed-off-by: Chuck Lever +Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 7511cce76fbbf..129a8c778d32d 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -1366,9 +1366,14 @@ void tls_sw_splice_eof(struct socket *sock) + mutex_unlock(&tls_ctx->tx_lock); + } + ++/* When has_copied is true the caller has already moved bytes to ++ * userspace. Report sk_err but leave it set so the next read ++ * surfaces it instead of a spurious EOF, otherwise sk_err is ++ * consumed via sock_error(). ++ */ + static int + tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, +- bool released) ++ bool released, bool has_copied) + { + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); +@@ -1382,8 +1387,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + if (!sk_psock_queue_empty(psock)) + return 0; + +- if (sk->sk_err) ++ if (sk->sk_err) { ++ if (has_copied) ++ return -READ_ONCE(sk->sk_err); + return sock_error(sk); ++ } + + if (ret < 0) + return ret; +@@ -1419,7 +1427,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + } + + if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) +- return tls_rx_rec_wait(sk, psock, nonblock, false); ++ return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); + + return 1; + } +@@ -2077,7 +2085,7 @@ int tls_sw_recvmsg(struct sock *sk, + int to_decrypt, chunk; + + err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, +- released); ++ released, !!(decrypted + copied)); + if (err <= 0) { + if (psock) { + chunk = sk_msg_recvmsg(sk, psock, msg, len, +@@ -2264,7 +2272,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct tls_decrypt_arg darg; + + err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, +- true); ++ true, false); + if (err <= 0) + goto splice_read_end; + +@@ -2350,7 +2358,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, + } else { + struct tls_decrypt_arg darg; + +- err = tls_rx_rec_wait(sk, NULL, true, released); ++ err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); + if (err <= 0) + goto read_sock_end; + +-- +2.53.0 + diff --git a/queue-6.12/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-6.12/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..85b0534027 --- /dev/null +++ b/queue-6.12/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 3a7313d4cb6805ff8350ce808c220e4b7314a319 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index 2d085115afde3..2a1e3537332d5 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1347,10 +1347,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-6.12/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch b/queue-6.12/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch new file mode 100644 index 0000000000..2b52449265 --- /dev/null +++ b/queue-6.12/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch @@ -0,0 +1,52 @@ +From 78e1695f6b9ac0e46f66ba76fda34eaab7bba50d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 22:48:43 +0800 +Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter + validation + +From: Ming Lei + +[ Upstream commit 1860c2f85922917d8a46f16a6f4bd2298ffa0fb5 ] + +blk_validate_limits() requires max_hw_sectors >= PAGE_SECTORS and fires +a WARN_ON_ONCE if this invariant is violated. ublk_validate_params() +only checked the upper bound of max_sectors against max_io_buf_bytes, +allowing userspace to pass small values (including zero) that trigger +the warning when blk_mq_alloc_disk() is called from +ublk_ctrl_start_dev(). + +Before 494ea040bcb5, ublk used blk_queue_max_hw_sectors() which silently +clamped small values up to PAGE_SECTORS. The conversion to passing +queue_limits directly to blk_mq_alloc_disk() lost that clamping and now +hits blk_validate_limits()'s WARN_ON_ONCE instead. + +Validate that max_sectors is at least PAGE_SECTORS in +ublk_validate_params() so invalid values are rejected early with +-EINVAL instead of reaching the block layer. + +Fixes: 494ea040bcb5 ("ublk: pass queue_limits to blk_mq_alloc_disk") +Signed-off-by: Ming Lei +Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + drivers/block/ublk_drv.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index c6a59f02944fc..6854b847bccef 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -540,6 +540,9 @@ static int ublk_validate_params(const struct ublk_device *ub) + if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) + return -EINVAL; + ++ if (p->max_sectors < PAGE_SECTORS) ++ return -EINVAL; ++ + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) + return -EINVAL; + } else +-- +2.53.0 + diff --git a/queue-6.12/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch b/queue-6.12/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch new file mode 100644 index 0000000000..0b5b75fe0e --- /dev/null +++ b/queue-6.12/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch @@ -0,0 +1,78 @@ +From fa6f43032f1eb21180ff81b555c7a448f5aa30a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 14:17:37 +0800 +Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged + +From: Kang Yang + +[ Upstream commit 54a5b38e4396530e5b2f12b54d3844e860ab6784 ] + +In ath10k_wmi_cmd_send(), the current code detects ATH10K_STATE_WEDGED +and sets ret to -ESHUTDOWN, but still proceeds to transmit pending +beacons and calls ath10k_wmi_cmd_send_nowait(). + +This can lead to incorrect behavior, as WMI commands and beacons are +still sent after the device has been marked as wedged, and the original +-ESHUTDOWN return value may be overwritten by the result of the send +path. + +The wedged state indicates the hardware is already unreliable, and no +further interaction with firmware is expected or meaningful in this +state. + +Fix this by skipping beacon transmission and the WMI send path entirely +once ATH10K_STATE_WEDGED is detected, ensuring consistent return values +and avoiding unnecessary firmware interaction. + +Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1 +Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189 + +Fixes: c256a94d1b1b ("wifi: ath10k: shutdown driver when hardware is unreliable") +Signed-off-by: Kang Yang +Reviewed-by: Rameshkumar Sundaram +Reviewed-by: Baochen Qiang +Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath10k/wmi.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c +index 408f062a4306f..c9f41309d18d4 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -3,7 +3,6 @@ + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +@@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) + ret = -ESHUTDOWN; + ath10k_dbg(ar, ATH10K_DBG_WMI, + "drop wmi command %d, hardware is wedged\n", cmd_id); +- } +- /* try to send pending beacons first. they take priority */ +- ath10k_wmi_tx_beacons_nowait(ar); ++ } else { ++ /* try to send pending beacons first. they take priority */ ++ ath10k_wmi_tx_beacons_nowait(ar); + +- ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); +- +- if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) +- ret = -ESHUTDOWN; ++ ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + ++ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) ++ ret = -ESHUTDOWN; ++ } + (ret != -EAGAIN); + }), 3 * HZ); + +-- +2.53.0 + diff --git a/queue-6.12/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch b/queue-6.12/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch new file mode 100644 index 0000000000..2cbf737f45 --- /dev/null +++ b/queue-6.12/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch @@ -0,0 +1,39 @@ +From 57c0d01dba23f788c8069157d5ca4865ebeb7544 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:40 +0200 +Subject: wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm() + +From: Nicolas Escande + +[ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ] + +This is similar to what was fixed by previous patches. We have a call +to ath11k_wmi_cmd_send() which does check the return value, but forgot +to free the related skb on error. + +Fixes: b43310e44edc ("wifi: ath11k: factory test mode support") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/testmode.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c +index 302d66092b973..7aa62a7d9a272 100644 +--- a/drivers/net/wireless/ath/ath11k/testmode.c ++++ b/drivers/net/wireless/ath/ath11k/testmode.c +@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret); ++ dev_kfree_skb(skb); + goto out; + } + +-- +2.53.0 + diff --git a/queue-6.12/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-6.12/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..56bed8642c --- /dev/null +++ b/queue-6.12/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,77 @@ +From ea0d4de68317700c6f89c14118c177d0d9510a74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 3b41bc5b125f4..5f15f7acd5132 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -9191,6 +9191,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9204,14 +9205,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9226,7 +9233,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +-- +2.53.0 + diff --git a/queue-6.12/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch b/queue-6.12/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch new file mode 100644 index 0000000000..ae1081d166 --- /dev/null +++ b/queue-6.12/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch @@ -0,0 +1,73 @@ +From f4598860dc83b16679f8f2457aeb059701142e65 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:50:35 +0100 +Subject: wifi: ath11k: fix peer resolution on rx path when peer_id=0 + +From: Matthew Leach + +[ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] + +It has been observed that on certain chipsets a peer can be assigned +peer_id=0. For reception of non-aggregated MPDUs this is fine as +ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer +based upon the source MAC address. On an aggregated link, the mpdu_start +header is only populated by hardware on the first sub-MSDU. This causes +the peer resolution to be skipped for the subsequent MSDUs and the +encryption type of these frames to be set to an incorrect value, +resulting in these MSDUs being dropped by ieee80211. + +ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 +ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 + +Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and +ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID +of 0 to be resolved. + +Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 + +Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") +Reviewed-by: Baochen Qiang +Signed-off-by: Matthew Leach +Reviewed-by: P Praneesh +Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- + drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index e3eabd9e223aa..78b0fa8d4f339 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2214,8 +2214,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) + + lockdep_assert_held(&ab->base_lock); + +- if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + + if (peer) + return peer; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 753bd93f02123..51e0840bc0d1e 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; +- u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); +- if (peer_id) +- ppdu_info->peer_id = peer_id; ++ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +-- +2.53.0 + diff --git a/queue-6.12/wifi-mac80211-fix-mle-defragmentation.patch b/queue-6.12/wifi-mac80211-fix-mle-defragmentation.patch new file mode 100644 index 0000000000..dbdc9b8c91 --- /dev/null +++ b/queue-6.12/wifi-mac80211-fix-mle-defragmentation.patch @@ -0,0 +1,160 @@ +From 8b6338cfd5e7cd81d2aad418d4ba4bb545ae0f6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:10:31 +0200 +Subject: wifi: mac80211: fix MLE defragmentation + +From: Johannes Berg + +[ Upstream commit a74e893f30db64cdce0fc7a96d3baa417bcd55f5 ] + +If either reconf or EPCS multi-link element (MLE) is contained in +a non-transmitted profile, the defragmentation routine is called +with a pointer to the defragmented copy, but the original elements. + +This is incorrect for two reasons: + - if the original defragmentation was needed, it will not find the + correct data + - if the original frame is at a higher address, the parsing will + potentially overrun the heap data (though given the layout of + the buffers, only into the new defragmentation buffer, and then + it has to stop and fail once that's filled with copied data. + +Fix it by tracking the container along with the pointer and in +doing so also unify the two almost identical defragmentation +routines. + +Fixes: 4d70e9c5488d ("wifi: mac80211: defragment reconfiguration MLE when parsing") +Reviewed-by: Miriam Rachel Korenblit +Reviewed-by: Ilan Peer +Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/parse.c | 71 +++++++++++++++++++------------------------- + 1 file changed, 31 insertions(+), 40 deletions(-) + +diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c +index 922ea9a6e2412..530f01575bc17 100644 +--- a/net/mac80211/parse.c ++++ b/net/mac80211/parse.c +@@ -34,6 +34,13 @@ + #include "led.h" + #include "wep.h" + ++struct ieee80211_elem_defrag { ++ const struct element *elem; ++ /* container start/len */ ++ const u8 *start; ++ size_t len; ++}; ++ + struct ieee80211_elems_parse { + /* must be first for kfree to work */ + struct ieee802_11_elems elems; +@@ -41,11 +48,7 @@ struct ieee80211_elems_parse { + /* The basic Multi-Link element in the original elements */ + const struct element *ml_basic_elem; + +- /* The reconfiguration Multi-Link element in the original elements */ +- const struct element *ml_reconf_elem; +- +- /* The EPCS Multi-Link element in the original elements */ +- const struct element *ml_epcs_elem; ++ struct ieee80211_elem_defrag ml_reconf, ml_epcs; + + bool multi_link_inner; + bool skip_vendor; +@@ -162,10 +165,14 @@ ieee80211_parse_extension_element(u32 *crc, + } + break; + case IEEE80211_ML_CONTROL_TYPE_RECONF: +- elems_parse->ml_reconf_elem = elem; ++ elems_parse->ml_reconf.elem = elem; ++ elems_parse->ml_reconf.start = params->start; ++ elems_parse->ml_reconf.len = params->len; + break; + case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: +- elems_parse->ml_epcs_elem = elem; ++ elems_parse->ml_epcs.elem = elem; ++ elems_parse->ml_epcs.start = params->start; ++ elems_parse->ml_epcs.len = params->len; + break; + default: + break; +@@ -950,46 +957,27 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, + sub->start, sub->len); + } + +-static void +-ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse) +-{ +- struct ieee802_11_elems *elems = &elems_parse->elems; +- ssize_t ml_len; +- +- ml_len = cfg80211_defragment_element(elems_parse->ml_reconf_elem, +- elems->ie_start, +- elems->total_len, +- elems_parse->scratch_pos, +- elems_parse->scratch + +- elems_parse->scratch_len - +- elems_parse->scratch_pos, +- WLAN_EID_FRAGMENT); +- if (ml_len < 0) +- return; +- elems->ml_reconf = (void *)elems_parse->scratch_pos; +- elems->ml_reconf_len = ml_len; +- elems_parse->scratch_pos += ml_len; +-} +- +-static void +-ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse) ++static const void * ++ieee80211_mle_defrag(struct ieee80211_elems_parse *elems_parse, ++ struct ieee80211_elem_defrag *defrag, ++ size_t *out_len) + { +- struct ieee802_11_elems *elems = &elems_parse->elems; ++ const void *ret; + ssize_t ml_len; + +- ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem, +- elems->ie_start, +- elems->total_len, ++ ml_len = cfg80211_defragment_element(defrag->elem, ++ defrag->start, defrag->len, + elems_parse->scratch_pos, + elems_parse->scratch + + elems_parse->scratch_len - + elems_parse->scratch_pos, + WLAN_EID_FRAGMENT); + if (ml_len < 0) +- return; +- elems->ml_epcs = (void *)elems_parse->scratch_pos; +- elems->ml_epcs_len = ml_len; ++ return NULL; ++ ret = elems_parse->scratch_pos; ++ *out_len = ml_len; + elems_parse->scratch_pos += ml_len; ++ return ret; + } + + struct ieee802_11_elems * +@@ -1069,9 +1057,12 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); + } + +- ieee80211_mle_defrag_reconf(elems_parse); +- +- ieee80211_mle_defrag_epcs(elems_parse); ++ elems->ml_reconf = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_reconf, ++ &elems->ml_reconf_len); ++ elems->ml_epcs = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_epcs, ++ &elems->ml_epcs_len); + + if (elems->tim && !elems->parse_error) { + const struct ieee80211_tim_ie *tim_ie = elems->tim; +-- +2.53.0 + diff --git a/queue-6.12/x86-xen-fix-xen_e820_swap_entry_with_ram.patch b/queue-6.12/x86-xen-fix-xen_e820_swap_entry_with_ram.patch new file mode 100644 index 0000000000..cb5d8b1781 --- /dev/null +++ b/queue-6.12/x86-xen-fix-xen_e820_swap_entry_with_ram.patch @@ -0,0 +1,39 @@ +From 6eef618dcaf4310ae4cb1fec531ccb81b1014528 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 12:24:17 +0200 +Subject: x86/xen: Fix xen_e820_swap_entry_with_ram() + +From: Juergen Gross + +[ Upstream commit 28e03f78e69cf6628b81f24777799778528a84c1 ] + +When swapping a not page-aligned E820 map entry with RAM, the start +address of the modified entry is calculated wrong (the offset into the +page is subtracted instead of being added to the page address). + +Fixes: be35d91c8880 ("xen: tolerate ACPI NVS memory overlapping with Xen allocated memory") +Reported-by: Jan Beulich +Reviewed-by: Jan Beulich +Signed-off-by: Juergen Gross +Message-ID: <20260505102417.208138-1-jgross@suse.com> +Signed-off-by: Sasha Levin +--- + arch/x86/xen/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c +index 3823e52aef523..6260f65a78c5e 100644 +--- a/arch/x86/xen/setup.c ++++ b/arch/x86/xen/setup.c +@@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) + /* Fill new entry (keep size and page offset). */ + entry->type = swap_entry->type; + entry->addr = entry_end - swap_size + +- swap_addr - swap_entry->addr; ++ swap_entry->addr - swap_addr; + entry->size = swap_entry->size; + + /* Convert old entry to RAM, align to pages. */ +-- +2.53.0 + diff --git a/queue-6.12/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch b/queue-6.12/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch new file mode 100644 index 0000000000..8daafe54b7 --- /dev/null +++ b/queue-6.12/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch @@ -0,0 +1,51 @@ +From 3540689a8fb4572599820e3b0332b0dd95f2d5e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 22:58:15 +0200 +Subject: zonefs: handle integer overflow in zonefs_fname_to_fno + +From: Johannes Thumshirn + +[ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ] + +In zonefs the file name in one of the two directories corresponds to the +zone number. + +Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), +where the parsing of the zone number from the file name can overflow the +'long' data type. + +Add a check for integer overflows and if the fno 'long' did overflow +return -ENOENT. + +Reported-by: Alexey Dobriyan +Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed") +Signed-off-by: Johannes Thumshirn +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + fs/zonefs/super.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c +index faf1eb87895d0..72408d8f9345c 100644 +--- a/fs/zonefs/super.c ++++ b/fs/zonefs/super.c +@@ -610,10 +610,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname) + return c - '0'; + + for (i = 0, rname = name + len - 1; i < len; i++, rname--) { ++ long digit; ++ + c = *rname; + if (!isdigit(c)) + return -ENOENT; +- fno += (c - '0') * shift; ++ digit = (c - '0') * shift; ++ if (check_add_overflow(fno, digit, &fno)) ++ return -ENOENT; + shift *= 10; + } + +-- +2.53.0 + diff --git a/queue-6.18/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch b/queue-6.18/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch new file mode 100644 index 0000000000..5e4892b885 --- /dev/null +++ b/queue-6.18/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch @@ -0,0 +1,78 @@ +From 3a7c84dad10d6eb621b502e4cd8c1c756e941246 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:39:01 -0700 +Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap + +From: Zack McKevitt + +[ Upstream commit aa16b2bc0f02709919e2435f531406531e5bcc69 ] + +The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to +(re)mapping beyond the VMA if the BO is too large. This can cause use +after free issues when munmap() unmaps only the VMA region and not the +additional mappings. To prevent this, check the remaining size of the +VMA before remapping and truncate the remapped length if sg->length is +too large. + +Reported-by: Lukas Maar +Fixes: ff13be830333 ("accel/qaic: Add datapath") +Reviewed-by: Karol Wachowski +Signed-off-by: Zack McKevitt +Reviewed-by: Jeff Hugo +[jhugo: fix braces from checkpatch --strict] +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_data.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c +index c4f117edb266e..3335c92b0d7dc 100644 +--- a/drivers/accel/qaic/qaic_data.c ++++ b/drivers/accel/qaic/qaic_data.c +@@ -605,8 +605,11 @@ static const struct vm_operations_struct drm_vm_ops = { + static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) + { + struct qaic_bo *bo = to_qaic_bo(obj); ++ unsigned long remap_start; + unsigned long offset = 0; ++ unsigned long remap_end; + struct scatterlist *sg; ++ unsigned long length; + int ret = 0; + + if (drm_gem_is_imported(obj)) +@@ -614,11 +617,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc + + for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { + if (sg_page(sg)) { ++ /* if sg is too large for the VMA, so truncate it to fit */ ++ if (check_add_overflow(vma->vm_start, offset, &remap_start)) ++ return -EINVAL; ++ if (check_add_overflow(remap_start, sg->length, &remap_end)) ++ return -EINVAL; ++ ++ if (remap_end > vma->vm_end) { ++ if (check_sub_overflow(vma->vm_end, remap_start, &length)) ++ return -EINVAL; ++ } else { ++ length = sg->length; ++ } ++ ++ if (length == 0) ++ goto out; ++ + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), +- sg->length, vma->vm_page_prot); ++ length, vma->vm_page_prot); + if (ret) + goto out; +- offset += sg->length; ++ offset += length; + } + } + +-- +2.53.0 + diff --git a/queue-6.18/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch b/queue-6.18/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch new file mode 100644 index 0000000000..e74028f7c3 --- /dev/null +++ b/queue-6.18/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch @@ -0,0 +1,48 @@ +From a43899e730e98266082fa20f7e7a193074cfa308 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:12:38 +0800 +Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node + +From: Shuhao Fu + +[ Upstream commit fca7401fe37f7abc6e54147ea560f37279231137 ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers must balance it with acpi_dev_put(). + +cs35l41_hda_read_acpi() stores the returned ACPI device in +cs35l41->dacpi. That reference is normally released by the later +probe cleanup or the remove path, but the NULL-check on +physdev exits before either of those paths can run. + +Drop the lookup reference before returning -ENODEV. + +Fixes: c34b04cc6178 ("ALSA: hda: cs35l41: Fix NULL pointer dereference in cs35l41_hda_read_acpi()") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/side-codecs/cs35l41_hda.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c +index 21e00055c0c44..47263c5a021cb 100644 +--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c ++++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c +@@ -1901,8 +1901,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i + + cs35l41->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); +- if (!physdev) ++ if (!physdev) { ++ acpi_dev_put(adev); + return -ENODEV; ++ } + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) +-- +2.53.0 + diff --git a/queue-6.18/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch b/queue-6.18/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch new file mode 100644 index 0000000000..0b51caf533 --- /dev/null +++ b/queue-6.18/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch @@ -0,0 +1,44 @@ +From 3a748f764fac8031927d61a90721c01be7416ef3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:01:39 +0800 +Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion + +From: Shuhao Fu + +[ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers are expected to balance it with acpi_dev_put(). + +When no companion is already attached, cs35l56_hda_read_acpi() looks +up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves +the lookup reference held. + +ACPI_COMPANION_SET() does not take ownership of that reference, so +drop it with acpi_dev_put() after attaching the companion. + +Fixes: 73cfbfa9caea ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/side-codecs/cs35l56_hda.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c +index 79c15e21d4bcb..1d25fe01066ee 100644 +--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c ++++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c +@@ -949,6 +949,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) + return -ENODEV; + } + ACPI_COMPANION_SET(cs35l56->base.dev, adev); ++ acpi_dev_put(adev); + } + + /* Initialize things that could be overwritten by a fixup */ +-- +2.53.0 + diff --git a/queue-6.18/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch b/queue-6.18/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch new file mode 100644 index 0000000000..271acddca1 --- /dev/null +++ b/queue-6.18/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch @@ -0,0 +1,57 @@ +From 8bd903dfdc02c93f48b35868f55b043bc110e6ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 19:15:31 +0800 +Subject: ALSA: hda/realtek: Use ALC287_FIXUP_TXNW2781_I2C for ASUS Strix Gxx5 + +From: Eric Naim + +[ Upstream commit 4372286ac774536e8e68bc6dfa0f0b0152b31fce ] + +These devices were incorrectly using the ALC287_FIXUP_TAS2781_I2C quirk +leading to errors: + +[ 18.765990] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768153] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768476] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768899] Serial bus multi instantiate pseudo device driver TXNW2781:00: Instantiated 3 I2C devices. + +Use the ALC287_FIXUP_TXNW2781_I2C quirk instead to fix this and restore +speaker audio on affected devices. + +Fixes: 1e9c708dc3ae ("ALSA: hda/tas2781: Add new quirk for Lenovo, ASUS, Dell projects") +Link: https://lore.kernel.org/59fd4aa4-76b9-4984-8db9-a60e55ec6e80@losource.net/ +Closes: https://lore.kernel.org/CACB9z7kjs8rhLstEc8fV29BCTb5dd881JwGozoKdO5cwCb=YwQ@mail.gmail.com +Signed-off-by: Eric Naim +Link: https://patch.msgid.link/20260516111532.111463-1-dnaim@cachyos.org +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/realtek/alc269.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index ea98cbc4310df..4cd5b719556ec 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -7182,12 +7182,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), +-- +2.53.0 + diff --git a/queue-6.18/alsa-scarlett2-add-missing-error-check-when-initiali.patch b/queue-6.18/alsa-scarlett2-add-missing-error-check-when-initiali.patch new file mode 100644 index 0000000000..d2ca90fa2b --- /dev/null +++ b/queue-6.18/alsa-scarlett2-add-missing-error-check-when-initiali.patch @@ -0,0 +1,41 @@ +From 3dc487f491bd2d1b2c1d4ba9eb799427cdf89f77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 10:39:14 +0700 +Subject: ALSA: scarlett2: Add missing error check when initialise Autogain + Status + +From: Robertus Diawan Chris + +[ Upstream commit c0e4fffc0f474b7ed10adee4ab2bc1a66d36fc72 ] + +When initialise new control with scarlett2_add_new_ctl() function for +Autogain Status, scarlett2_add_new_ctl() might throw an error. So, add +error check after initialise new control for Autogain Status. + +This is reported by Coverity Scan with CID 1598781 as UNUSED_VALUE. + +Fixes: 0a995e38dc44 ("ALSA: scarlett2: Add support for software-controllable input gain") +Signed-off-by: Robertus Diawan Chris +Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer_scarlett2.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c +index d0cad1d1efa35..da3c2741ef575 100644 +--- a/sound/usb/mixer_scarlett2.c ++++ b/sound/usb/mixer_scarlett2.c +@@ -6707,6 +6707,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_status_ctl, + i, 1, s, &private->autogain_status_ctls[i]); ++ if (err < 0) ++ return err; + } + + /* Add autogain target controls */ +-- +2.53.0 + diff --git a/queue-6.18/alsa-seq-serialize-ump-output-teardown-with-event_in.patch b/queue-6.18/alsa-seq-serialize-ump-output-teardown-with-event_in.patch new file mode 100644 index 0000000000..58732c93e1 --- /dev/null +++ b/queue-6.18/alsa-seq-serialize-ump-output-teardown-with-event_in.patch @@ -0,0 +1,174 @@ +From fc8ed58fac2ec82d8eba90c022257df48dc6aacb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:32:49 +0800 +Subject: ALSA: seq: Serialize UMP output teardown with event_input + +From: Zhang Cen + +[ Upstream commit 60a1969fae6209644698fca91c185d153674f631 ] + +seq_ump_process_event() borrows client->out_rfile.output without +synchronizing with the first-open and last-close transition in +seq_ump_client_open() and seq_ump_client_close(). + +The last output unuse can therefore drop opened[STR_OUT] to zero and +release the rawmidi file while an in-flight event_input callback is still +inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream +runtime exposed to teardown before the write path has taken its own +buffer reference. + +Add a per-client rwlock for the event_input-visible output file. Publish +a newly opened output file under the write side, and hold the read side +from the output lookup through snd_rawmidi_kernel_write(). The last +output close copies and clears the visible output file under the write +side, then drops the lock and releases the saved rawmidi file. Use +IRQ-safe rwlock guards because event_input can also be reached from +atomic sequencer delivery. + +The buggy scenario involves two paths, with each column showing the +order within that path: + +path A label: event_input path path B label: last unuse path +1. seq_ump_process_event() reads 1. seq_ump_client_close() + client->out_rfile.output. drops opened[STR_OUT] to zero. +2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release() + has not yet pinned runtime. closes the output file. +3. The writer continues using 3. close_substream() frees + the borrowed substream. substream->runtime. + +This keeps the output substream and runtime alive for the full +event_input write while keeping rawmidi release outside the rwlock. + +KASAN reproduced this as a slab-use-after-free in +snd_rawmidi_kernel_write1(), with allocation through +seq_ump_use()/snd_seq_port_connect() and free through +seq_ump_unuse()/snd_seq_port_disconnect(). + +Suggested-by: Takashi Iwai + +Validation reproduced this kernel report: +KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400 +RIP: 0033:0x7f5528af837f +Read of size 8 +Call trace: + dump_stack_lvl+0x73/0xb0 (?:?) + print_report+0xd1/0x650 (?:?) + srso_alias_return_thunk+0x5/0xfbef5 (?:?) + __virt_addr_valid+0x1a7/0x340 (?:?) + kasan_complete_mode_report_info+0x64/0x200 (?:?) + kasan_report+0xf7/0x130 (?:?) + snd_rawmidi_kernel_write1+0x9d/0x400 (?:?) + __asan_load8+0x82/0xb0 (?:?) + update_stack_state+0x1ef/0x2d0 (?:?) + snd_rawmidi_kernel_write+0x1a/0x20 (?:?) + seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82) + __snd_seq_deliver_single_event+0x8a/0xe0 (?:?) + snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?) + lock_acquire+0x14e/0x2e0 (?:?) + find_held_lock+0x31/0x90 (?:?) + snd_seq_port_use_ptr+0xa6/0xe0 (?:?) + __kasan_check_write+0x18/0x20 (?:?) + do_raw_read_unlock+0x32/0xa0 (?:?) + _raw_read_unlock+0x26/0x50 (?:?) + snd_seq_deliver_single_event+0x45c/0x4b0 (?:?) + snd_seq_deliver_event+0x10d/0x1b0 (?:?) + snd_seq_client_enqueue_event+0x192/0x240 (?:?) + snd_seq_write+0x2cd/0x450 (?:?) + apparmor_file_permission+0x20/0x30 (?:?) + security_file_permission+0x51/0x60 (?:?) + vfs_write+0x1ce/0x850 (?:?) + __fget_files+0x12b/0x220 (?:?) + lock_release+0xc8/0x2a0 (?:?) + __rcu_read_unlock+0x74/0x2d0 (?:?) + __fget_files+0x135/0x220 (?:?) + ksys_write+0x15a/0x180 (?:?) + rcu_is_watching+0x24/0x60 (?:?) + __x64_sys_write+0x46/0x60 (?:?) + x64_sys_call+0x7d/0x20d0 (?:?) + do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) + entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) + +Fixes: 81fd444aa371 ("ALSA: seq: Bind UMP device") +Signed-off-by: Zhang Cen +Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/core/seq/seq_ump_client.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c +index ebbe1cbe0b9b7..9a6b4b61bd74c 100644 +--- a/sound/core/seq/seq_ump_client.c ++++ b/sound/core/seq/seq_ump_client.c +@@ -37,6 +37,7 @@ struct seq_ump_client { + struct snd_ump_endpoint *ump; /* assigned endpoint */ + int seq_client; /* sequencer client id */ + int opened[2]; /* current opens for each direction */ ++ rwlock_t output_lock; /* protects out_rfile output access */ + struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + struct seq_ump_input_buffer input; /* input parser context */ + void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ +@@ -88,6 +89,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + unsigned char type; + int len; + ++ guard(read_lock_irqsave)(&client->output_lock); + substream = client->out_rfile.output; + if (!substream) + return -ENODEV; +@@ -106,6 +108,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + static int seq_ump_client_open(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + int err; + + guard(mutex)(&ump->open_mutex); +@@ -113,9 +116,11 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + err = snd_rawmidi_kernel_open(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, +- &client->out_rfile); ++ &rfile); + if (err < 0) + return err; ++ scoped_guard(write_lock_irqsave, &client->output_lock) ++ client->out_rfile = rfile; + } + client->opened[dir]++; + return 0; +@@ -125,11 +130,19 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + static int seq_ump_client_close(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + + guard(mutex)(&ump->open_mutex); +- if (!--client->opened[dir]) +- if (dir == STR_OUT) +- snd_rawmidi_kernel_release(&client->out_rfile); ++ if (!--client->opened[dir]) { ++ if (dir == STR_OUT) { ++ scoped_guard(write_lock_irqsave, &client->output_lock) { ++ rfile = client->out_rfile; ++ client->out_rfile = (struct snd_rawmidi_file){}; ++ } ++ if (rfile.rmidi) ++ snd_rawmidi_kernel_release(&rfile); ++ } ++ } + return 0; + } + +@@ -468,6 +481,7 @@ static int snd_seq_ump_probe(struct device *_dev) + + INIT_WORK(&client->group_notify_work, handle_group_notify); + client->ump = ump; ++ rwlock_init(&client->output_lock); + + client->seq_client = + snd_seq_create_kernel_client(card, ump->core.device, +-- +2.53.0 + diff --git a/queue-6.18/arm-dts-renesas-genmai-drop-superfluous-cells.patch b/queue-6.18/arm-dts-renesas-genmai-drop-superfluous-cells.patch new file mode 100644 index 0000000000..409be6d4d1 --- /dev/null +++ b/queue-6.18/arm-dts-renesas-genmai-drop-superfluous-cells.patch @@ -0,0 +1,40 @@ +From 671667a4d847a9da5fd46e60e4a74cd37c5d62cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:10 +0100 +Subject: ARM: dts: renesas: genmai: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit 714e1d6bba0e0abe5c87c8e189a35fa690540df4 ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-genmai.dts:28.17-55.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +index 3c37565097145..da552a66615e0 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +@@ -34,9 +34,6 @@ flash@18000000 { + clocks = <&mstp9_clks R7S72100_CLK_SPIBSC0>; + power-domains = <&cpg_clocks>; + +- #address-cells = <1>; +- #size-cells = <1>; +- + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; +-- +2.53.0 + diff --git a/queue-6.18/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch b/queue-6.18/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch new file mode 100644 index 0000000000..01c7b4acf9 --- /dev/null +++ b/queue-6.18/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch @@ -0,0 +1,39 @@ +From a26c87834582be29994b73cf1c58e69ba6eef9d9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:11 +0100 +Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit ab83176d3cf1cf1c1f6e604432905bda4515d17f ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts:32.17-72.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +index 91178fb9e7210..3306bc9b7bc37 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +@@ -36,8 +36,6 @@ flash@18000000 { + power-domains = <&cpg_clocks>; + bank-width = <4>; + device-width = <1>; +- #address-cells = <1>; +- #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; +-- +2.53.0 + diff --git a/queue-6.18/arm-integrator-fix-early-initialization.patch b/queue-6.18/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..ff94d71292 --- /dev/null +++ b/queue-6.18/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From bc5b96682da3b53826d0ae91d91f9b231426fcf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c +index 2ed4ded56b3fe..03dfb5f720b7b 100644 +--- a/arch/arm/mach-versatile/integrator_cp.c ++++ b/arch/arm/mach-versatile/integrator_cp.c +@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-6.18/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch b/queue-6.18/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch new file mode 100644 index 0000000000..efda498910 --- /dev/null +++ b/queue-6.18/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch @@ -0,0 +1,46 @@ +From 2eaa80cc175b819925dbae9ae8d832161b969c3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 13:42:02 -0300 +Subject: ASoC: amd: acp-sdw-legacy: check CPU DAI name before logging +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Cássio Gabriel + +[ Upstream commit 1afd8f06dcb1d561af3b239c5b14a88b87c13454 ] + +devm_kasprintf() can fail and return NULL. The legacy AMD SoundWire +machine driver logs cpus->dai_name before checking the allocation result. + +Move the debug print after the NULL check, matching the ordering used by +the SOF AMD SoundWire path after commit 5726b68473f7 ("ASoC: amd/sdw_utils: +avoid NULL deref when devm_kasprintf() fails"). + +Fixes: 2981d9b0789c ("ASoC: amd: acp: add soundwire machine driver for legacy stack") +Signed-off-by: Cássio Gabriel +Link: https://patch.msgid.link/20260511-asoc-amd-acp-sdw-legacy-dai-name-null-v1-1-dc6151b6da8a@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +index 798c73d7e26de..e87d9e9991e1c 100644 +--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +@@ -244,9 +244,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SDW%d Pin%d", + link_num, cpu_pin_id); +- dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); + if (!cpus->dai_name) + return -ENOMEM; ++ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); + + codec_maps[j].cpu = 0; + codec_maps[j].codec = j; +-- +2.53.0 + diff --git a/queue-6.18/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch b/queue-6.18/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch new file mode 100644 index 0000000000..221e52c1e4 --- /dev/null +++ b/queue-6.18/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch @@ -0,0 +1,44 @@ +From 5f709685e086e8436f5eeb1d5b5c46f122b134be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 21:08:52 +0200 +Subject: ASoC: codecs: fs210x: fix possible buffer overflow + +From: Alexander A. Klimov + +[ Upstream commit 0d435a7ebcd4e97e47673c1ab6fb27f973a053ec ] + +In fs210x_effect_scene_info(), a string was copied like this: + + strscpy(DST, SRC, strlen(SRC) + 1); + +A buffer overflow would happen if strlen(SRC) >= sizeof(DST). +Actually, strscpy() must be used this way: + + strscpy(DST, SRC, sizeof(DST)); + strscpy(DST, SRC); // defaults to sizeof(DST) + +Fixes: 756117701779 ("ASoC: codecs: Add FourSemi FS2104/5S audio amplifier driver") +Signed-off-by: Alexander A. Klimov +Link: https://patch.msgid.link/20260513190852.196723-2-grandmaster@al2klimov.de +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/fs210x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c +index e2f85714972d5..e2207c53c50d5 100644 +--- a/sound/soc/codecs/fs210x.c ++++ b/sound/soc/codecs/fs210x.c +@@ -968,7 +968,7 @@ static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol, + if (scene->name) + name = scene->name; + +- strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1); ++ strscpy(uinfo->value.enumerated.name, name); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.18/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch b/queue-6.18/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch new file mode 100644 index 0000000000..5ae2f71b1d --- /dev/null +++ b/queue-6.18/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch @@ -0,0 +1,48 @@ +From 01ad3ae55fb442ac86eef7a3c2f38d95aca90bde Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:30:57 +0100 +Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove() + +From: Richard Fitzgerald + +[ Upstream commit 18e7bd9f2446664053f8c34b72abd4606d22d858 ] + +Use flush_work() instead of cancel_work_sync() to terminate pending IRQ +work in cs35l56_sdw_remove(). And flush_work() again after masking the +interrupts to flush any queueing that was racing with the masking. This is +the same sequence as cs35l56_sdw_system_suspend(). + +cs35l56_sdw_interrupt() takes the pm_runtime to prevent the bus powering- +down before the interrupt status can be read and handled. The work releases +this pm_runtime. So cancelling it, instead of flushing, could leave an +unbalanced pm_runtime. + +Signed-off-by: Richard Fitzgerald +Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") +Link: https://patch.msgid.link/20260521123057.988732-1-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs35l56-sdw.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +index 42d24ac2977fc..a513a15d7e5a7 100644 +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -560,10 +560,11 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; +- cancel_work_sync(&cs35l56->sdw_irq_work); ++ flush_work(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ flush_work(&cs35l56->sdw_irq_work); + + cs35l56_remove(cs35l56); + +-- +2.53.0 + diff --git a/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch b/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch new file mode 100644 index 0000000000..dfba97f135 --- /dev/null +++ b/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch @@ -0,0 +1,41 @@ +From 764695e61fdac16b6e00a386e792db3525731675 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 17:32:23 +0800 +Subject: ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC + +From: Mac Chiang + +[ Upstream commit 9c37daee7c17fa17e8d41089ee1f658b06cb672a ] + +Some devices do not use CODEC_MIC but use the host PCH_DMIC +instead. Add a quirk to skip the CODEC_MIC DAI when it is not present +in disco table, ensuring the correct capture device is used. + +If CODEC_MIC is present, it continues to be used as default. + +Fixes: 9489db97f6f0 ("ASoC: sdw_utils: add SmartMic DAI for RT712 VB") +Signed-off-by: Mac Chiang +Signed-off-by: Bard Liao +Link: https://patch.msgid.link/20260508093224.1246282-2-yung-chuan.liao@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index 3848c7df1916f..050ca17ff105d 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -170,6 +170,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, ++ .quirk = SOC_SDW_CODEC_MIC, ++ .quirk_exclude = true, + }, + }, + .dai_num = 3, +-- +2.53.0 + diff --git a/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch b/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch new file mode 100644 index 0000000000..928585dbbd --- /dev/null +++ b/queue-6.18/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch @@ -0,0 +1,39 @@ +From 896a43f74d7e75b9e0614a7b1074b9599fba3794 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 17:32:24 +0800 +Subject: ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC + +From: Mac Chiang + +[ Upstream commit fa749a77bdc50f0d695aaf81f1bd55967d77d10f ] + +Add a quirk to skip the CODEC_MIC DAI when it is not present. +This ensures PCH_DMIC is used as the fallback; otherwise, +CODEC_MIC remains the default. + +Fixes: 846a8d3cf3ba ("ASoC: Intel: soc-acpi-intel-ptl-match: Add rt721 support") +Signed-off-by: Mac Chiang +Signed-off-by: Bard Liao +Link: https://patch.msgid.link/20260508093224.1246282-3-yung-chuan.liao@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index 050ca17ff105d..3facb78748acf 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -431,6 +431,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, ++ .quirk = SOC_SDW_CODEC_MIC, ++ .quirk_exclude = true, + }, + }, + .dai_num = 3, +-- +2.53.0 + diff --git a/queue-6.18/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch b/queue-6.18/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch new file mode 100644 index 0000000000..7941cff398 --- /dev/null +++ b/queue-6.18/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch @@ -0,0 +1,39 @@ +From 6fddb67521bc17c9e3b31388bf1cce769cab3a3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 12:40:24 +0700 +Subject: ASoC: soc-utils: Add missing va_end in snd_soc_ret() + +From: Robertus Diawan Chris + +[ Upstream commit 298a43b54432fbc3a32949a94c72544ee18c8c00 ] + +The default case in snd_soc_ret() use va_start without va_end to +cleanup "args" object which can cause undefined behavior. So, add +missing va_end to cleanup "args" object. + +This is reported by Coverity Scan as "Missing varargs init or cleanup". + +Fixes: 943116ba2a6a ("ASoC: add common snd_soc_ret() and use it") +Signed-off-by: Robertus Diawan Chris +Link: https://patch.msgid.link/20260519054024.274741-1-robertusdchris@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/soc-utils.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c +index c8adfff826bd4..9cb7567e263eb 100644 +--- a/sound/soc/soc-utils.c ++++ b/sound/soc/soc-utils.c +@@ -36,6 +36,7 @@ int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...) + vaf.va = &args; + + dev_err(dev, "ASoC error (%d): %pV", ret, &vaf); ++ va_end(args); + } + + return ret; +-- +2.53.0 + diff --git a/queue-6.18/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch b/queue-6.18/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch new file mode 100644 index 0000000000..31f58d725d --- /dev/null +++ b/queue-6.18/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch @@ -0,0 +1,47 @@ +From 87df7e658f74a29a799f0e6e7cc2efa532612dd7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:36:36 -0500 +Subject: ASoC: SOF: amd: Fix error code handling in psp_send_cmd() + +From: Mario Limonciello + +[ Upstream commit 2c7b1227e582e88db7917412dca4e752c1aff691 ] + +The smn_read_register() helper returns negative error codes on failure +or the register value on success. When used with read_poll_timeout(), +the return value is stored in the 'data' variable. + +Currently 'data' is declared as u32, which causes negative error codes +to be cast to large positive values. This makes the condition 'data > 0' +incorrectly treat errors as success. + +Fix by changing 'data' from u32 to int, matching the pattern used in +psp_mbox_ready() which correctly handles the same helper function. + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/linux-sound/agGES8vWrLOrBu28@stanley.mountain/ +Fixes: f120cf33d232 ("ASoC: SOF: amd: Use AMD_NODE") +Signed-off-by: Mario Limonciello +Link: https://patch.msgid.link/20260511153638.724810-1-mario.limonciello@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sof/amd/acp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +index 71a18f156de23..f615b8d1c8020 100644 +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -223,7 +223,7 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) + { + struct snd_sof_dev *sdev = adata->dev; + int ret; +- u32 data; ++ int data; + + if (!cmd) + return -EINVAL; +-- +2.53.0 + diff --git a/queue-6.18/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch b/queue-6.18/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch new file mode 100644 index 0000000000..207a73bea6 --- /dev/null +++ b/queue-6.18/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch @@ -0,0 +1,72 @@ +From 96f21e9f8095c05aaae0559fe2ccfaa8ed5ecee3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 01:09:29 -0400 +Subject: block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user() + +From: Sungwoo Kim + +[ Upstream commit 8582792cf23b3d94674d4d838f7cde9a28d0fcaf ] + +pin_user_pages_fast() can partially succeed and return the number of +pages that were actually pinned. However, the bio_integrity_map_user() +does not handle this partial pinning. This leads to a general protection +fault since bvec_from_pages() dereferences an unpinned page address, +which is 0. + +To fix this, add a check to verify that all requested memory is pinned. +If partial pinning occurs, unpin the memory and return -EFAULT. + +Kernel Oops: + +Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy) +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 +RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6 + +Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers") +Acked-by: Chao Shi +Acked-by: Weidong Zhu +Acked-by: Dave Tian +Signed-off-by: Sungwoo Kim +Tested-by: Shin'ichiro Kawasaki +Link: https://github.com/linux-blktests/blktests/pull/244 +Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index ab4a15437925e..46c59ed92bd1c 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -299,6 +299,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + if (unlikely(ret < 0)) + goto free_bvec; + ++ /* ++ * Handle partial pinning. This can happen when pin_user_pages_fast() ++ * returns fewer pages than requested. ++ */ ++ if (user_backed_iter(iter) && unlikely(ret != bytes)) { ++ if (ret > 0) { ++ int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ int i; ++ ++ for (i = 0; i < npinned; i++) ++ unpin_user_page(pages[i]); ++ } ++ if (pages != stack_pages) ++ kvfree(pages); ++ ret = -EFAULT; ++ goto free_bvec; ++ } ++ + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, + &is_p2p); + if (pages != stack_pages) +-- +2.53.0 + diff --git a/queue-6.18/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch b/queue-6.18/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch new file mode 100644 index 0000000000..1385c81a4e --- /dev/null +++ b/queue-6.18/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch @@ -0,0 +1,43 @@ +From 557c8b0c76d143a7e6567409ada497c7c41bef0d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 22:51:51 +0100 +Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user() + +From: David Carlier + +[ Upstream commit 637ad3a56a3b889527d1dacea6fea2a8bd648140 ] + +bio_integrity_add_page() already sets bip_vcnt to 1 for the bounce +segment. Overwriting it with nr_vecs breaks bip_vcnt <= bip_max_vcnt +on WRITE (bip_max_vcnt is 1), so the gap-merge checks in block/blk.h +read past the bip_vec[] flex array. On READ the read is in bounds +but lands on a saved user bvec instead of the bounce. + +The line was added for split propagation, but bio_integrity_clone() +doesn't copy bip_vcnt and BIP_CLONE_FLAGS excludes BIP_COPY_USER. + +Fixes: 3991657ae707 ("block: set bip_vcnt correctly") +Signed-off-by: David Carlier +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 12d887349c260..ab4a15437925e 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -205,7 +205,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + } + + bip->bip_flags |= BIP_COPY_USER; +- bip->bip_vcnt = nr_vecs; + return 0; + free_bip: + bio_integrity_free(bio); +-- +2.53.0 + diff --git a/queue-6.18/block-recompute-nr_integrity_segments-in-blk_insert_.patch b/queue-6.18/block-recompute-nr_integrity_segments-in-blk_insert_.patch new file mode 100644 index 0000000000..9c7e28f677 --- /dev/null +++ b/queue-6.18/block-recompute-nr_integrity_segments-in-blk_insert_.patch @@ -0,0 +1,82 @@ +From ce6146039a4c8c2980339186a3639eb0c1434092 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:22:30 -0600 +Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request + +From: Casey Chen + +[ Upstream commit 2c6e6a18a37b905cb584eb0dda3ae482162a81ca ] + +blk_insert_cloned_request() already recomputes nr_phys_segments +against the bottom queue, because "the queue settings related to +segment counting may differ from the original queue." The exact same +reasoning applies to integrity segments: a stacked driver's underlying +queue can have tighter virt_boundary_mask, seg_boundary_mask, or +max_segment_size than the top queue, in which case +blk_rq_count_integrity_sg() against the bottom queue produces a +different count than the cached rq->nr_integrity_segments inherited +from the source request by blk_rq_prep_clone(). + +When the cached count is lower than the bottom queue's actual count, +blk_rq_map_integrity_sg() trips + + BUG_ON(segments > rq->nr_integrity_segments); + +on dispatch. The same families of stacked setups that motivated the +existing nr_phys_segments recompute -- dm-multipath fanning out to +nvme-rdma in particular -- can produce this. + +Mirror the nr_phys_segments handling: when the request carries +integrity, recompute nr_integrity_segments against the bottom queue +and reject the request if it exceeds the bottom queue's +max_integrity_segments. blk_rq_count_integrity_sg() and +queue_max_integrity_segments() are both already available via +, which blk-mq.c includes. + +This closes a latent gap in the stacking contract and brings the +integrity-segment accounting in line with the existing +phys-segment accounting. + +Fixes: 76c313f658d2 ("blk-integrity: improved sg segment mapping") +Signed-off-by: Casey Chen +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-mq.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 4ebb92014eae1..ab05c5c9e6ae2 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -3285,6 +3285,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq) + return BLK_STS_IOERR; + } + ++ /* ++ * Integrity segment counting depends on the same queue limits ++ * (virt_boundary_mask, seg_boundary_mask, max_segment_size) that ++ * vary across stacked queues, so recompute against the bottom ++ * queue just like nr_phys_segments above. ++ */ ++ if (blk_integrity_rq(rq) && rq->bio) { ++ unsigned short max_int_segs = queue_max_integrity_segments(q); ++ ++ rq->nr_integrity_segments = ++ blk_rq_count_integrity_sg(rq->q, rq->bio); ++ if (rq->nr_integrity_segments > max_int_segs) { ++ printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n", ++ __func__, rq->nr_integrity_segments, ++ max_int_segs); ++ return BLK_STS_IOERR; ++ } ++ } ++ + if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq))) + return BLK_STS_IOERR; + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch b/queue-6.18/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch new file mode 100644 index 0000000000..0f509eb652 --- /dev/null +++ b/queue-6.18/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch @@ -0,0 +1,82 @@ +From a526a872f281ccdd246f4caeb317639e87db71d0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 00:32:48 +0530 +Subject: Bluetooth: btintel_pcie: Fix incorrect MAC access programming + +From: Kiran K + +[ Upstream commit 88365d04fdc821dc4e9eb0cc00fdf6905430d172 ] + +btintel_pcie_get_mac_access() and btintel_pcie_release_mac_access() +were programming STOP_MAC_ACCESS_DIS and XTAL_CLK_REQ in addition to +the MAC_ACCESS_REQ handshake. These bits are not part of the host +MAC-access handshake on the supported parts; the driver was +programming them incorrectly. Drop the writes so the register update +contains only the bits the controller actually consumes. + +Fixes: b9465e6670a2 ("Bluetooth: btintel_pcie: Read hardware exception data") +Signed-off-by: Kiran K +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel_pcie.c | 20 ++++++-------------- + drivers/bluetooth/btintel_pcie.h | 3 --- + 2 files changed, 6 insertions(+), 17 deletions(-) + +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +index c68a8de3025b0..d0aa1666d6ac6 100644 +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -567,12 +567,10 @@ static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data) + + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); + +- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; +- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; +- if ((reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS) == 0) ++ if (!(reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ)) { + reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; +- +- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ } + + do { + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); +@@ -592,16 +590,10 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data) + + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); + +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) ++ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) { + reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; +- +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS) +- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; +- +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ) +- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; +- +- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ } + } + + static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type, +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +index 48e1ae1793e5c..481de85456e2b 100644 +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -34,9 +34,6 @@ + #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20)) + + #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21)) +-/* Stop MAC Access disconnection request */ +-#define BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS (BIT(22)) +-#define BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ (BIT(23)) + + #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS (BIT(28)) + #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29)) +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch b/queue-6.18/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch new file mode 100644 index 0000000000..b8b44a12f7 --- /dev/null +++ b/queue-6.18/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch @@ -0,0 +1,43 @@ +From 40f28155e6e90f5a4a2096a3f311cd6242df32a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:24:02 +0800 +Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths + +From: Jiajia Liu + +[ Upstream commit dd1dda6b8d6e1f4376a5b3055a04f0ecbdb4d6bd ] + +The setup_packet of control urb is not freed if usb_submit_urb fails or +the submitted urb is killed. Add free in these two paths. + +Fixes: a1c49c434e150 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") +Signed-off-by: Jiajia Liu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index cf05746e9db5a..38ba8f2bd2f96 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -496,6 +496,7 @@ static void btmtk_usb_wmt_recv(struct urb *urb) + return; + } else if (urb->status == -ENOENT) { + /* Avoid suspend failed when usb_kill_urb */ ++ kfree(urb->setup_packet); + return; + } + +@@ -569,6 +570,7 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); ++ kfree(dr); + usb_unanchor_urb(urb); + } + +-- +2.53.0 + diff --git a/queue-6.18/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch b/queue-6.18/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch new file mode 100644 index 0000000000..a494f52541 --- /dev/null +++ b/queue-6.18/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch @@ -0,0 +1,82 @@ +From d29ccbe7ffe9f18ca6fb1ffc5297fa10456ba6b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 23:56:26 +0900 +Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx + +From: Xingwang Xiang + +[ Upstream commit ddf8029623a1af20e984c040e89ff918158397ab ] + +sk_psock_strp_data_ready() already checks tls_sw_has_ctx_rx() and +defers to psock->saved_data_ready when a TLS RX context is present, +avoiding a conflict with the TLS strparser's ownership of the receive +queue (commit e91de6afa81c, "bpf: Fix running sk_skb program types +with ktls"). + +sk_psock_verdict_data_ready() has no equivalent guard. When a socket +is inserted into a sockmap (BPF_SK_SKB_VERDICT) before TLS RX is +configured, tls_sw_strparser_arm() saves sk_psock_verdict_data_ready +as rx_ctx->saved_data_ready. On data arrival: + + tls_data_ready -> tls_strp_data_ready -> tls_rx_msg_ready + -> saved_data_ready() = sk_psock_verdict_data_ready() + -> tcp_read_skb() drains sk_receive_queue via __skb_unlink() + without calling tcp_eat_skb(), so copied_seq is not advanced. + +tls_strp_msg_load() then finds tcp_inq() >= full_len (stale), calls +tcp_recv_skb() on the now-empty queue, hits WARN_ON_ONCE(!first), and +returns with rx_ctx->strp.anchor.frag_list pointing at a psock-owned +(potentially freed) skb. tls_decrypt_sg() subsequently walks that +frag_list: use-after-free. + +Apply the same fix as sk_psock_strp_data_ready(): if a TLS RX context +is present, call psock->saved_data_ready (sock_def_readable) to wake +recv() waiters and return immediately, leaving the receive queue +untouched. TLS retains sole ownership of the queue and decrypts the +record normally through tls_sw_recvmsg(). + +Fixes: ef5659280eb1 ("bpf, sockmap: Allow skipping sk_skb parser program") +Signed-off-by: Xingwang Xiang +Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/skmsg.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/core/skmsg.c b/net/core/skmsg.c +index 35a6acbf9a579..75ea4fdb27642 100644 +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -1268,12 +1268,19 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) + static void sk_psock_verdict_data_ready(struct sock *sk) + { + const struct proto_ops *ops = NULL; ++ struct sk_psock *psock; + struct socket *sock; + int copied; + + trace_sk_data_ready(sk); + + rcu_read_lock(); ++ psock = sk_psock(sk); ++ if (psock && tls_sw_has_ctx_rx(sk)) { ++ psock->saved_data_ready(sk); ++ rcu_read_unlock(); ++ return; ++ } + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); +@@ -1283,8 +1290,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) + + copied = ops->read_skb(sk, sk_psock_verdict_recv); + if (copied >= 0) { +- struct sk_psock *psock; +- + rcu_read_lock(); + psock = sk_psock(sk); + if (psock) +-- +2.53.0 + diff --git a/queue-6.18/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-6.18/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..39276d92f0 --- /dev/null +++ b/queue-6.18/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From ceb9be3dd8c925a54eb47474ee671b144e726fa1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index e9a7e65304017..b77b3250afbd2 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4640,10 +4640,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4651,7 +4665,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4668,7 +4681,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4676,8 +4689,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-6.18/btrfs-add-macros-to-facilitate-printing-of-keys.patch b/queue-6.18/btrfs-add-macros-to-facilitate-printing-of-keys.patch new file mode 100644 index 0000000000..11ae81eedd --- /dev/null +++ b/queue-6.18/btrfs-add-macros-to-facilitate-printing-of-keys.patch @@ -0,0 +1,54 @@ +From 6ef44a8f8dafed7e390f724d5d7e66acf0f50fbe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Oct 2025 12:40:06 +0100 +Subject: btrfs: add macros to facilitate printing of keys + +From: Filipe Manana + +[ Upstream commit 95de4b097e25225d4deb5a33a4bfc27bb441f2d8 ] + +There's a lot of places where we need to print a key, and it's tiresome +to type the format specifier, typically "(%llu %u %llu)", as well as +passing 3 arguments to a prink family function (key->objectid, key->type, +key->offset). + +So add a couple macros for this just like we have for csum values in +btrfs_inode.h (CSUM_FMT and CSUM_FMT_VALUE). + +This also ensures that we consistently print a key in the same format, +always as "(%llu %llu %llu)", which is the most common format we use, but +we have a few variations such as "[%llu %llu %llu]" for no good reason. + +This patch introduces the macros while the next one makes use of it. +This is to ease backports of future patches, since then we can backport +this patch which is simple and short and then backport those future +patches, as the next patch in the series that makes use of these new +macros is quite large and may have some dependencies. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/fs.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h +index 37aa8d141a83d..acdb9e52f6fd7 100644 +--- a/fs/btrfs/fs.h ++++ b/fs/btrfs/fs.h +@@ -73,6 +73,9 @@ struct btrfs_space_info; + #define BTRFS_SUPER_INFO_SIZE 4096 + static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE); + ++#define BTRFS_KEY_FMT "(%llu %u %llu)" ++#define BTRFS_KEY_FMT_VALUE(key) (key)->objectid, (key)->type, (key)->offset ++ + /* + * Number of metadata items necessary for an unlink operation: + * +-- +2.53.0 + diff --git a/queue-6.18/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch b/queue-6.18/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch new file mode 100644 index 0000000000..61adc20c21 --- /dev/null +++ b/queue-6.18/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch @@ -0,0 +1,130 @@ +From 84882fb9a9c3e9aac1e7b357d60147e0419cb7a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 13:07:11 -0700 +Subject: btrfs: check for subvolume before deleting squota qgroup + +From: Boris Burkov + +[ Upstream commit 1e92637722ae4bd417f7a37e8d1485dc23b93935 ] + +The invariant that we want to maintain with subvolume qgroups is that +the qgroup can only be deleted if there is no root. With squotas, we +thought that it was sufficient to just check the usage, because we +assumed that deleting a subvolume will drive it's qgroups usage to 0, +and thus 0 usage implies no subvolume. + +However, this is false, for two reasons: + +- A subvol whose extents are all from before squotas was enabled. +- A subvol that was created in this transaction and for which we have + not yet run any delayed refs. + +In both cases, deleting the qgroup breaks the desired invariant and we +are left with a subvolume with no qgroup but squotas are enabled. + +Fix this by unifying the deletion check logic between full qgroups and +squotas. Squotas do all the same checks *and* the additional usage == 0 +check, which is the one extra rule peculiar to squotas. + +Link: https://lore.kernel.org/linux-btrfs/adnBhWfJQ1n3hZC8@merlins.org/ +Fixes: a8df35619948 ("btrfs: forbid deleting live subvol qgroup") +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 50 +++++++++++++++++++++++------------------------ + 1 file changed, 25 insertions(+), 25 deletions(-) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index c7bc2065bcd68..a0d364e009d88 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1723,32 +1723,24 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) + return ret; + } + +-static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) +- ++static bool can_delete_parent_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) + { + ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); ++ if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ++ squota_check_parent_usage(fs_info, qgroup); + return list_empty(&qgroup->members); + } + + /* +- * Return true if we can delete the squota qgroup and false otherwise. +- * +- * Rules for whether we can delete: +- * +- * A subvolume qgroup can be removed iff the subvolume is fully deleted, which +- * is iff there is 0 usage in the qgroup. +- * +- * A higher level qgroup can be removed iff it has no members. +- * Note: We audit its usage to warn on inconsitencies without blocking deletion. ++ * Because a shared extent can outlive its owning subvolume, we cannot delete a ++ * subvol squota qgroup until all of the extents it owns are gone, even if the ++ * subvolume itself has been deleted. + */ +-static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) ++static bool can_delete_squota_subvol_qgroup(struct btrfs_fs_info *fs_info, ++ struct btrfs_qgroup *qgroup) + { + ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); +- +- if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { +- squota_check_parent_usage(fs_info, qgroup); +- return can_delete_parent_qgroup(qgroup); +- } ++ ASSERT(btrfs_qgroup_level(qgroup->qgroupid) == 0); + + return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); + } +@@ -1762,14 +1754,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + { + struct btrfs_key key; + BTRFS_PATH_AUTO_FREE(path); +- +- /* Since squotas cannot be inconsistent, they have special rules for deletion. */ +- if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) +- return can_delete_squota_qgroup(fs_info, qgroup); ++ int ret; + + /* For higher level qgroup, we can only delete it if it has no child. */ + if (btrfs_qgroup_level(qgroup->qgroupid)) +- return can_delete_parent_qgroup(qgroup); ++ return can_delete_parent_qgroup(fs_info, qgroup); + + /* + * For level-0 qgroups, we can only delete it if it has no subvolume +@@ -1785,10 +1774,21 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + return -ENOMEM; + + /* +- * The @ret from btrfs_find_root() exactly matches our definition for +- * the return value, thus can be returned directly. ++ * Any subvol qgroup, regardless of mode, cannot be deleted if the ++ * subvol still exists. ++ */ ++ ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); ++ /* ++ * btrfs_find_root returns <0 on error, 0 if found, and >0 if not, ++ * so the "found" and "error" cases match our desired return values. + */ +- return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); ++ if (ret <= 0) ++ return ret; ++ ++ /* Squotas require additional checks, even if the subvol is deleted. */ ++ if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ++ return can_delete_squota_subvol_qgroup(fs_info, qgroup); ++ return 1; + } + + int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) +-- +2.53.0 + diff --git a/queue-6.18/btrfs-check-squota-parent-usage-on-membership-change.patch b/queue-6.18/btrfs-check-squota-parent-usage-on-membership-change.patch new file mode 100644 index 0000000000..515324707f --- /dev/null +++ b/queue-6.18/btrfs-check-squota-parent-usage-on-membership-change.patch @@ -0,0 +1,91 @@ +From 3a6070c11ebae7080fa399d78f43ee304f13274a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Dec 2025 15:33:49 -0800 +Subject: btrfs: check squota parent usage on membership change + +From: Boris Burkov + +[ Upstream commit 9c46bcda5f347febdbb4d117fb21a37ffcec5fa4 ] + +We could have detected the quick inherit bug more directly if we had +an extra warning about squota hierarchy consistency while modifying the +hierarchy. In squotas, the parent usage always simply adds up to the sum of +its children, so we can just check for that when changing membership and +detect more accounting bugs. + +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 7b62d2faaf7ec..db1df67bccf38 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -346,6 +346,42 @@ int btrfs_verify_qgroup_counts(const struct btrfs_fs_info *fs_info, u64 qgroupid + } + #endif + ++static bool squota_check_parent_usage(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *parent) ++{ ++ u64 excl_sum = 0; ++ u64 rfer_sum = 0; ++ u64 excl_cmpr_sum = 0; ++ u64 rfer_cmpr_sum = 0; ++ struct btrfs_qgroup_list *glist; ++ int nr_members = 0; ++ bool mismatch; ++ ++ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) ++ return false; ++ if (btrfs_qgroup_level(parent->qgroupid) == 0) ++ return false; ++ ++ /* Eligible parent qgroup. Squota; level > 0; empty members list. */ ++ list_for_each_entry(glist, &parent->members, next_member) { ++ excl_sum += glist->member->excl; ++ rfer_sum += glist->member->rfer; ++ excl_cmpr_sum += glist->member->excl_cmpr; ++ rfer_cmpr_sum += glist->member->rfer_cmpr; ++ nr_members++; ++ } ++ mismatch = (parent->excl != excl_sum || parent->rfer != rfer_sum || ++ parent->excl_cmpr != excl_cmpr_sum || parent->rfer_cmpr != excl_cmpr_sum); ++ ++ WARN(mismatch, ++ "parent squota qgroup %hu/%llu has mismatched usage from its %d members. " ++ "%llu %llu %llu %llu vs %llu %llu %llu %llu\n", ++ btrfs_qgroup_level(parent->qgroupid), ++ btrfs_qgroup_subvolid(parent->qgroupid), nr_members, parent->excl, ++ parent->rfer, parent->excl_cmpr, parent->rfer_cmpr, excl_sum, ++ rfer_sum, excl_cmpr_sum, rfer_cmpr_sum); ++ return mismatch; ++} ++ + __printf(2, 3) + static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info, const char *fmt, ...) + { +@@ -1565,6 +1601,7 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst + goto out; + } + ret = quick_update_accounting(fs_info, src, dst, 1); ++ squota_check_parent_usage(fs_info, parent); + spin_unlock(&fs_info->qgroup_lock); + out: + kfree(prealloc); +@@ -1623,6 +1660,8 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, + spin_lock(&fs_info->qgroup_lock); + del_relation_rb(fs_info, src, dst); + ret = quick_update_accounting(fs_info, src, dst, -1); ++ ASSERT(parent); ++ squota_check_parent_usage(fs_info, parent); + spin_unlock(&fs_info->qgroup_lock); + } + out: +-- +2.53.0 + diff --git a/queue-6.18/btrfs-don-t-search-back-for-dir-inode-item-in-ino_lo.patch b/queue-6.18/btrfs-don-t-search-back-for-dir-inode-item-in-ino_lo.patch new file mode 100644 index 0000000000..3ff5151536 --- /dev/null +++ b/queue-6.18/btrfs-don-t-search-back-for-dir-inode-item-in-ino_lo.patch @@ -0,0 +1,85 @@ +From 5ab4d125d72f697037bb2d15521c57cd21bcb658 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Nov 2025 17:08:43 +0100 +Subject: btrfs: don't search back for dir inode item in INO_LOOKUP_USER + +From: Josef Bacik + +[ Upstream commit 70085399b1a1623ef488d96b4c2d0c67be1d0607 ] + +We don't need to search back to the inode item, the directory inode +number is in key.offset, so simply use that. If we can't find the +directory we'll get an ENOENT at the iget(). + +Note: The patch was taken from v5 of fscrypt patchset +(https://lore.kernel.org/linux-btrfs/cover.1706116485.git.josef@toxicpanda.com/) +which was handled over time by various people: Omar Sandoval, Sweet Tea +Dorminy, Josef Bacik. + +Reviewed-by: Johannes Thumshirn +Signed-off-by: Josef Bacik +Signed-off-by: Daniel Vacek +Reviewed-by: David Sterba +[ add note ] +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/ioctl.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index c0691e93e0a58..41e549d37aac8 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -1832,7 +1832,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + struct btrfs_root_ref *rref; + struct btrfs_root *root = NULL; + struct btrfs_path *path; +- struct btrfs_key key, key2; ++ struct btrfs_key key; + struct extent_buffer *leaf; + char *ptr; + int slot; +@@ -1887,24 +1887,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + read_extent_buffer(leaf, ptr, + (unsigned long)(iref + 1), len); + +- /* Check the read+exec permission of this directory */ +- ret = btrfs_previous_item(root, path, dirid, +- BTRFS_INODE_ITEM_KEY); +- if (ret < 0) { +- goto out_put; +- } else if (ret > 0) { +- ret = -ENOENT; +- goto out_put; +- } +- +- leaf = path->nodes[0]; +- slot = path->slots[0]; +- btrfs_item_key_to_cpu(leaf, &key2, slot); +- if (key2.objectid != dirid) { +- ret = -ENOENT; +- goto out_put; +- } +- + /* + * We don't need the path anymore, so release it and + * avoid deadlocks and lockdep warnings in case +@@ -1912,11 +1894,12 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + * btree and lock the same leaf. + */ + btrfs_release_path(path); +- temp_inode = btrfs_iget(key2.objectid, root); ++ temp_inode = btrfs_iget(key.offset, root); + if (IS_ERR(temp_inode)) { + ret = PTR_ERR(temp_inode); + goto out_put; + } ++ /* Check the read+exec permission of this directory. */ + ret = inode_permission(idmap, &temp_inode->vfs_inode, + MAY_READ | MAY_EXEC); + iput(&temp_inode->vfs_inode); +-- +2.53.0 + diff --git a/queue-6.18/btrfs-fix-squota-accounting-during-enable-generation.patch b/queue-6.18/btrfs-fix-squota-accounting-during-enable-generation.patch new file mode 100644 index 0000000000..12d79029bc --- /dev/null +++ b/queue-6.18/btrfs-fix-squota-accounting-during-enable-generation.patch @@ -0,0 +1,130 @@ +From 68cba38c49e44cb3bff9344b09653b501ae1abfd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 19:53:46 -0700 +Subject: btrfs: fix squota accounting during enable generation + +From: Boris Burkov + +[ Upstream commit d7c600554816b8ef70adffe078a0e360c055d82b ] + +The first transaction that enables squotas is special and a bit tricky. +We have to set BTRFS_FS_QUOTA_ENABLED after the transaction to avoid a +deadlock, so any delayed refs that run before we set the bit are not +squota accounted. For data this is fine, we don't get an owner_ref, so +there is no real harm, it's as if the extent predated squotas. However +for metadata, the tree block will have gen == enable_gen so when we free +it later, we will decrement the squota accounting, which can result in +an underflow. Before it is freed, btrfs check shows errors, as we have +mismatched usage between the node generations/owners and the squota +values. + +There are two angles to this fix: + +1. For extents that come in delayed_refs that run during the + enable_gen transaction, we must actually set enable_gen to the *next* + transaction. That is the first transaction that we can really + properly account in any way. +2. For extents that come in between the end of our transaction handle + and the time we set the BTRFS_FS_QUOTA_ENABLED bit, we need an + additional bit, BTRFS_FS_SQUOTA_ENABLING which only affects recording + squota deltas, so we do pick up those extents. Otherwise, we would + miss them, even for enable_gen + 1. + +Fixes: bd7c1ea3a302 ("btrfs: qgroup: check generation when recording simple quota delta") +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/fs.h | 1 + + fs/btrfs/qgroup.c | 31 +++++++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 4 deletions(-) + +diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h +index acdb9e52f6fd7..eccc61463947b 100644 +--- a/fs/btrfs/fs.h ++++ b/fs/btrfs/fs.h +@@ -136,6 +136,7 @@ enum { + BTRFS_FS_LOG_RECOVERING, + BTRFS_FS_OPEN, + BTRFS_FS_QUOTA_ENABLED, ++ BTRFS_FS_SQUOTA_ENABLING, + BTRFS_FS_UPDATE_UUID_TREE_GEN, + BTRFS_FS_CREATING_FREE_SPACE_TREE, + BTRFS_FS_BTREE_ERR, +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index a0d364e009d88..261aa65019207 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1111,7 +1111,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + if (simple) { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE; + btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA); +- btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid); ++ /* ++ * Set the enable generation to the next transaction, as we cannot ++ * ensure that extents written during this transaction will see any ++ * state we have set here. So we should treat all extents of the ++ * transaction as coming in before squotas was enabled. ++ */ ++ btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1); + } else { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; + } +@@ -1214,7 +1220,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + goto out_free_path; + } + +- fs_info->qgroup_enable_gen = trans->transid; ++ /* ++ * Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING ++ * under the transaction handle. We want to ensure that all extents in ++ * the next transaction definitely see them. ++ */ ++ if (simple) { ++ fs_info->qgroup_enable_gen = trans->transid + 1; ++ set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ } + + mutex_unlock(&fs_info->qgroup_ioctl_lock); + /* +@@ -1228,9 +1242,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + */ + ret = btrfs_commit_transaction(trans); + trans = NULL; ++ + mutex_lock(&fs_info->qgroup_ioctl_lock); +- if (ret) ++ if (ret) { ++ if (simple) { ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ fs_info->qgroup_enable_gen = 0; ++ } + goto out_free_path; ++ } + + /* + * Set quota enabled flag after committing the transaction, to avoid +@@ -1240,6 +1260,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + spin_lock(&fs_info->qgroup_lock); + fs_info->quota_root = quota_root; + set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); ++ if (simple) ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); + spin_unlock(&fs_info->qgroup_lock); + + /* Skip rescan for simple qgroups. */ +@@ -4930,7 +4952,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info, + u64 num_bytes = delta->num_bytes; + const int sign = (delta->is_inc ? 1 : -1); + +- if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) ++ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE && ++ !test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags)) + return 0; + + if (!btrfs_is_fstree(root)) +-- +2.53.0 + diff --git a/queue-6.18/btrfs-relax-squota-parent-qgroup-deletion-rule.patch b/queue-6.18/btrfs-relax-squota-parent-qgroup-deletion-rule.patch new file mode 100644 index 0000000000..87876b9481 --- /dev/null +++ b/queue-6.18/btrfs-relax-squota-parent-qgroup-deletion-rule.patch @@ -0,0 +1,99 @@ +From 69c6500243680c54f80c673e36971960abce1adc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Dec 2025 15:35:02 -0800 +Subject: btrfs: relax squota parent qgroup deletion rule + +From: Boris Burkov + +[ Upstream commit adb0af40fe89fd42f1ef277bf60d9cfa7c2ae472 ] + +Currently, with squotas, we do not allow removing a parent qgroup with +no members if it still has usage accounted to it. This makes it really +difficult to recover from accounting bugs, as we have no good way of +getting back to 0 usage. + +Instead, allow deletion (it's safe at 0 members..) while still warning +about the inconsistency by adding a squota parent check. + +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 50 +++++++++++++++++++++++++++++++++-------------- + 1 file changed, 35 insertions(+), 15 deletions(-) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index db1df67bccf38..c7bc2065bcd68 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1723,6 +1723,36 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) + return ret; + } + ++static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) ++ ++{ ++ ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); ++ return list_empty(&qgroup->members); ++} ++ ++/* ++ * Return true if we can delete the squota qgroup and false otherwise. ++ * ++ * Rules for whether we can delete: ++ * ++ * A subvolume qgroup can be removed iff the subvolume is fully deleted, which ++ * is iff there is 0 usage in the qgroup. ++ * ++ * A higher level qgroup can be removed iff it has no members. ++ * Note: We audit its usage to warn on inconsitencies without blocking deletion. ++ */ ++static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) ++{ ++ ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); ++ ++ if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { ++ squota_check_parent_usage(fs_info, qgroup); ++ return can_delete_parent_qgroup(qgroup); ++ } ++ ++ return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); ++} ++ + /* + * Return 0 if we can not delete the qgroup (not empty or has children etc). + * Return >0 if we can delete the qgroup. +@@ -1733,23 +1763,13 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + struct btrfs_key key; + BTRFS_PATH_AUTO_FREE(path); + +- /* +- * Squota would never be inconsistent, but there can still be case +- * where a dropped subvolume still has qgroup numbers, and squota +- * relies on such qgroup for future accounting. +- * +- * So for squota, do not allow dropping any non-zero qgroup. +- */ +- if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE && +- (qgroup->rfer || qgroup->excl || qgroup->excl_cmpr || qgroup->rfer_cmpr)) +- return 0; ++ /* Since squotas cannot be inconsistent, they have special rules for deletion. */ ++ if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ++ return can_delete_squota_qgroup(fs_info, qgroup); + + /* For higher level qgroup, we can only delete it if it has no child. */ +- if (btrfs_qgroup_level(qgroup->qgroupid)) { +- if (!list_empty(&qgroup->members)) +- return 0; +- return 1; +- } ++ if (btrfs_qgroup_level(qgroup->qgroupid)) ++ return can_delete_parent_qgroup(qgroup); + + /* + * For level-0 qgroups, we can only delete it if it has no subvolume +-- +2.53.0 + diff --git a/queue-6.18/btrfs-remaining-btrfs_path_auto_free-conversions.patch b/queue-6.18/btrfs-remaining-btrfs_path_auto_free-conversions.patch new file mode 100644 index 0000000000..96fd11b82f --- /dev/null +++ b/queue-6.18/btrfs-remaining-btrfs_path_auto_free-conversions.patch @@ -0,0 +1,1051 @@ +From 96a2e89d4d2866cc740950c3c96e6c08e2f3f1d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Nov 2025 17:06:46 +0100 +Subject: btrfs: remaining BTRFS_PATH_AUTO_FREE conversions + +From: David Sterba + +[ Upstream commit 10934c131f9bcfb616dd8be9456f11efd6b240ec ] + +Do the remaining btrfs_path conversion to the auto cleaning, this seems +to be the last one. Most of the conversions are trivial, only adding the +declaration and removing the freeing, or changing the goto patterns to +return. + +There are some functions with many changes, like __btrfs_free_extent(), +btrfs_remove_from_free_space_tree() or btrfs_add_to_free_space_tree() +but it still follows the same pattern. + +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/block-group.c | 3 +- + fs/btrfs/dir-item.c | 3 +- + fs/btrfs/extent-tree.c | 41 +++++------ + fs/btrfs/free-space-tree.c | 29 ++++---- + fs/btrfs/inode-item.c | 3 +- + fs/btrfs/inode.c | 3 +- + fs/btrfs/ioctl.c | 37 ++++------ + fs/btrfs/qgroup.c | 142 ++++++++++++++----------------------- + fs/btrfs/super.c | 10 +-- + fs/btrfs/tree-log.c | 4 +- + fs/btrfs/volumes.c | 3 +- + fs/btrfs/xattr.c | 3 +- + 12 files changed, 105 insertions(+), 176 deletions(-) + +diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c +index a277c8cc91661..1e57f7d04c47e 100644 +--- a/fs/btrfs/block-group.c ++++ b/fs/btrfs/block-group.c +@@ -1065,7 +1065,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + struct btrfs_chunk_map *map) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_block_group *block_group; + struct btrfs_free_cluster *cluster; + struct inode *inode; +@@ -1305,7 +1305,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + btrfs_put_block_group(block_group); + if (remove_rsv) + btrfs_dec_delayed_refs_rsv_bg_updates(fs_info); +- btrfs_free_path(path); + return ret; + } + +diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c +index 77e1bcb2a74bf..085a83ae9e62f 100644 +--- a/fs/btrfs/dir-item.c ++++ b/fs/btrfs/dir-item.c +@@ -112,7 +112,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, + int ret = 0; + int ret2 = 0; + struct btrfs_root *root = dir->root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_dir_item *dir_item; + struct extent_buffer *leaf; + unsigned long name_ptr; +@@ -164,7 +164,6 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, + ret2 = btrfs_insert_delayed_dir_index(trans, name->name, name->len, dir, + &disk_key, type, index); + out_free: +- btrfs_free_path(path); + if (ret) + return ret; + if (ret2) +diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c +index e40835fe4bde6..663526d909ab1 100644 +--- a/fs/btrfs/extent-tree.c ++++ b/fs/btrfs/extent-tree.c +@@ -3084,7 +3084,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + { + struct btrfs_fs_info *info = trans->fs_info; + struct btrfs_key key; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_root *extent_root; + struct extent_buffer *leaf; + struct btrfs_extent_item *ei; +@@ -3119,7 +3119,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + node->bytenr, refs_to_drop); + ret = -EINVAL; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + if (is_data) +@@ -3164,15 +3164,14 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + abort_and_dump(trans, path, + "invalid iref slot %u, no EXTENT/METADATA_ITEM found but has inline extent ref", + path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + /* Must be SHARED_* item, remove the backref first */ + ret = remove_extent_backref(trans, extent_root, path, + NULL, refs_to_drop, is_data); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + btrfs_release_path(path); + +@@ -3221,7 +3220,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + } + if (unlikely(ret < 0)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + extent_slot = path->slots[0]; + } +@@ -3230,10 +3229,10 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + "unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu slot %d", + bytenr, node->parent, node->ref_root, owner_objectid, + owner_offset, path->slots[0]); +- goto out; ++ return ret; + } else { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + leaf = path->nodes[0]; +@@ -3244,7 +3243,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + "unexpected extent item size, has %u expect >= %zu", + item_size, sizeof(*ei)); + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + ei = btrfs_item_ptr(leaf, extent_slot, + struct btrfs_extent_item); +@@ -3258,8 +3257,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + key.objectid, key.type, key.offset, + path->slots[0], owner_objectid, item_size, + sizeof(*ei) + sizeof(*bi)); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + bi = (struct btrfs_tree_block_info *)(ei + 1); + WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi)); +@@ -3270,8 +3268,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + abort_and_dump(trans, path, + "trying to drop %d refs but we only have %llu for bytenr %llu slot %u", + refs_to_drop, refs, bytenr, path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + refs -= refs_to_drop; + +@@ -3287,8 +3284,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + abort_and_dump(trans, path, + "invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found, slot %u", + path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + } else { + btrfs_set_extent_refs(leaf, ei, refs); +@@ -3298,7 +3294,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + iref, refs_to_drop, is_data); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + } + } else { +@@ -3318,8 +3314,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + "invalid refs_to_drop, current refs %u refs_to_drop %u slot %u", + extent_data_ref_count(path, iref), + refs_to_drop, path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + if (iref) { + if (unlikely(path->slots[0] != extent_slot)) { +@@ -3327,8 +3322,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + "invalid iref, extent item key " BTRFS_KEY_FMT " slot %u doesn't have wanted iref", + BTRFS_KEY_FMT_VALUE(&key), + path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + } else { + /* +@@ -3341,8 +3335,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + abort_and_dump(trans, path, + "invalid SHARED_* item slot %u, previous item is not EXTENT/METADATA_ITEM", + path->slots[0]); +- ret = -EUCLEAN; +- goto out; ++ return -EUCLEAN; + } + path->slots[0] = extent_slot; + num_to_del = 2; +@@ -3363,7 +3356,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + num_to_del); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + btrfs_release_path(path); + +@@ -3371,8 +3364,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + } + btrfs_release_path(path); + +-out: +- btrfs_free_path(path); + return ret; + } + +diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c +index d86541073d42d..c3734892d6548 100644 +--- a/fs/btrfs/free-space-tree.c ++++ b/fs/btrfs/free-space-tree.c +@@ -841,7 +841,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, + u64 start, u64 size) + { + struct btrfs_block_group *block_group; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + int ret; + + if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) +@@ -851,7 +851,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, + if (unlikely(!path)) { + ret = -ENOMEM; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + block_group = btrfs_lookup_block_group(trans->fs_info, start); +@@ -859,7 +859,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, + DEBUG_WARN("no block group found for start=%llu", start); + ret = -ENOENT; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + mutex_lock(&block_group->free_space_lock); +@@ -869,8 +869,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, + btrfs_abort_transaction(trans, ret); + + btrfs_put_block_group(block_group); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +@@ -1023,7 +1022,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, + u64 start, u64 size) + { + struct btrfs_block_group *block_group; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + int ret; + + if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) +@@ -1033,7 +1032,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, + if (unlikely(!path)) { + ret = -ENOMEM; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + block_group = btrfs_lookup_block_group(trans->fs_info, start); +@@ -1041,7 +1040,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, + DEBUG_WARN("no block group found for start=%llu", start); + ret = -ENOENT; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + mutex_lock(&block_group->free_space_lock); +@@ -1051,8 +1050,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, + btrfs_abort_transaction(trans, ret); + + btrfs_put_block_group(block_group); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +@@ -1466,7 +1464,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, + struct btrfs_block_group *block_group) + { + struct btrfs_root *root = btrfs_free_space_root(block_group); +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key, found_key; + struct extent_buffer *leaf; + u64 start, end; +@@ -1485,7 +1483,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, + if (unlikely(!path)) { + ret = -ENOMEM; + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + start = block_group->start; +@@ -1499,7 +1497,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, + ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + + leaf = path->nodes[0]; +@@ -1530,14 +1528,13 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, + ret = btrfs_del_items(trans, root, path, path->slots[0], nr); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); +- goto out; ++ return ret; + } + btrfs_release_path(path); + } + + ret = 0; +-out: +- btrfs_free_path(path); ++ + return ret; + } + +diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c +index 1bd73b80f9fac..7e14e1bbcf389 100644 +--- a/fs/btrfs/inode-item.c ++++ b/fs/btrfs/inode-item.c +@@ -444,7 +444,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, + struct btrfs_truncate_control *control) + { + struct btrfs_fs_info *fs_info = root->fs_info; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct extent_buffer *leaf; + struct btrfs_file_extent_item *fi; + struct btrfs_key key; +@@ -730,6 +730,5 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, + if (!ret && control->last_size > new_size) + control->last_size = new_size; + +- btrfs_free_path(path); + return ret; + } +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index dcc0e53782c61..a4f1810db079d 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4448,7 +4448,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, + { + struct btrfs_root *root = dir->root; + struct btrfs_inode *inode = BTRFS_I(d_inode(dentry)); +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct extent_buffer *leaf; + struct btrfs_dir_item *di; + struct btrfs_key key; +@@ -4541,7 +4541,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, + if (ret) + btrfs_abort_transaction(trans, ret); + out: +- btrfs_free_path(path); + fscrypt_free_filename(&fname); + return ret; + } +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index 41e549d37aac8..2f1c5f5e2e725 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -1613,7 +1613,7 @@ static noinline int search_ioctl(struct btrfs_root *root, + { + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_key key; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + int ret; + int num_found = 0; + unsigned long sk_offset = 0; +@@ -1633,10 +1633,8 @@ static noinline int search_ioctl(struct btrfs_root *root, + } else { + /* Look up the root from the arguments. */ + root = btrfs_get_fs_root(info, sk->tree_id, true); +- if (IS_ERR(root)) { +- btrfs_free_path(path); ++ if (IS_ERR(root)) + return PTR_ERR(root); +- } + } + + key.objectid = sk->min_objectid; +@@ -1670,7 +1668,6 @@ static noinline int search_ioctl(struct btrfs_root *root, + + sk->nr_items = num_found; + btrfs_put_root(root); +- btrfs_free_path(path); + return ret; + } + +@@ -1753,7 +1750,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, + int total_len = 0; + struct btrfs_inode_ref *iref; + struct extent_buffer *l; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + + if (dirid == BTRFS_FIRST_FREE_OBJECTID) { + name[0]='\0'; +@@ -1814,7 +1811,6 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, + ret = 0; + out: + btrfs_put_root(root); +- btrfs_free_path(path); + return ret; + } + +@@ -1831,7 +1827,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + struct btrfs_inode_ref *iref; + struct btrfs_root_ref *rref; + struct btrfs_root *root = NULL; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct extent_buffer *leaf; + char *ptr; +@@ -1852,10 +1848,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1]; + + root = btrfs_get_fs_root(fs_info, treeid, true); +- if (IS_ERR(root)) { +- ret = PTR_ERR(root); +- goto out; +- } ++ if (IS_ERR(root)) ++ return PTR_ERR(root); + + key.objectid = dirid; + key.type = BTRFS_INODE_REF_KEY; +@@ -1930,12 +1924,10 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + key.type = BTRFS_ROOT_REF_KEY; + key.offset = args->treeid; + ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); +- if (ret < 0) { +- goto out; +- } else if (ret > 0) { +- ret = -ENOENT; +- goto out; +- } ++ if (ret < 0) ++ return ret; ++ else if (ret > 0) ++ return -ENOENT; + + leaf = path->nodes[0]; + slot = path->slots[0]; +@@ -1945,10 +1937,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + item_len = btrfs_item_size(leaf, slot); + /* Check if dirid in ROOT_REF corresponds to passed dirid */ + rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); +- if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) { +- ret = -EINVAL; +- goto out; +- } ++ if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) ++ return -EINVAL; + + /* Copy subvolume's name */ + item_off += sizeof(struct btrfs_root_ref); +@@ -1958,8 +1948,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, + + out_put: + btrfs_put_root(root); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 1f83af14568ca..7b62d2faaf7ec 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -660,7 +660,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, + { + int ret; + struct btrfs_root *quota_root = trans->fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + + path = btrfs_alloc_path(); +@@ -672,7 +672,6 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, + key.offset = dst; + + ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0); +- btrfs_free_path(path); + return ret; + } + +@@ -681,7 +680,7 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, + { + int ret; + struct btrfs_root *quota_root = trans->fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + + path = btrfs_alloc_path(); +@@ -694,24 +693,19 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, + + ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); + if (ret < 0) +- goto out; ++ return ret; + +- if (ret > 0) { +- ret = -ENOENT; +- goto out; +- } ++ if (ret > 0) ++ return -ENOENT; + +- ret = btrfs_del_item(trans, quota_root, path); +-out: +- btrfs_free_path(path); +- return ret; ++ return btrfs_del_item(trans, quota_root, path); + } + + static int add_qgroup_item(struct btrfs_trans_handle *trans, + struct btrfs_root *quota_root, u64 qgroupid) + { + int ret; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_qgroup_info_item *qgroup_info; + struct btrfs_qgroup_limit_item *qgroup_limit; + struct extent_buffer *leaf; +@@ -737,7 +731,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, + ret = btrfs_insert_empty_item(trans, quota_root, path, &key, + sizeof(*qgroup_info)); + if (ret && ret != -EEXIST) +- goto out; ++ return ret; + + leaf = path->nodes[0]; + qgroup_info = btrfs_item_ptr(leaf, path->slots[0], +@@ -754,7 +748,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, + ret = btrfs_insert_empty_item(trans, quota_root, path, &key, + sizeof(*qgroup_limit)); + if (ret && ret != -EEXIST) +- goto out; ++ return ret; + + leaf = path->nodes[0]; + qgroup_limit = btrfs_item_ptr(leaf, path->slots[0], +@@ -765,17 +759,14 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, + btrfs_set_qgroup_limit_rsv_rfer(leaf, qgroup_limit, 0); + btrfs_set_qgroup_limit_rsv_excl(leaf, qgroup_limit, 0); + +- ret = 0; +-out: +- btrfs_free_path(path); +- return ret; ++ return 0; + } + + static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid) + { + int ret; + struct btrfs_root *quota_root = trans->fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + + path = btrfs_alloc_path(); +@@ -787,33 +778,27 @@ static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid) + key.offset = qgroupid; + ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); + if (ret < 0) +- goto out; ++ return ret; + +- if (ret > 0) { +- ret = -ENOENT; +- goto out; +- } ++ if (ret > 0) ++ return -ENOENT; + + ret = btrfs_del_item(trans, quota_root, path); + if (ret) +- goto out; ++ return ret; + + btrfs_release_path(path); + + key.type = BTRFS_QGROUP_LIMIT_KEY; + ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); + if (ret < 0) +- goto out; ++ return ret; + +- if (ret > 0) { +- ret = -ENOENT; +- goto out; +- } ++ if (ret > 0) ++ return -ENOENT; + + ret = btrfs_del_item(trans, quota_root, path); + +-out: +- btrfs_free_path(path); + return ret; + } + +@@ -821,7 +806,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, + struct btrfs_qgroup *qgroup) + { + struct btrfs_root *quota_root = trans->fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct extent_buffer *l; + struct btrfs_qgroup_limit_item *qgroup_limit; +@@ -841,7 +826,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, + ret = -ENOENT; + + if (ret) +- goto out; ++ return ret; + + l = path->nodes[0]; + slot = path->slots[0]; +@@ -851,8 +836,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, + btrfs_set_qgroup_limit_max_excl(l, qgroup_limit, qgroup->max_excl); + btrfs_set_qgroup_limit_rsv_rfer(l, qgroup_limit, qgroup->rsv_rfer); + btrfs_set_qgroup_limit_rsv_excl(l, qgroup_limit, qgroup->rsv_excl); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +@@ -861,7 +845,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, + { + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_root *quota_root = fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct extent_buffer *l; + struct btrfs_qgroup_info_item *qgroup_info; +@@ -884,7 +868,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, + ret = -ENOENT; + + if (ret) +- goto out; ++ return ret; + + l = path->nodes[0]; + slot = path->slots[0]; +@@ -894,8 +878,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, + btrfs_set_qgroup_info_rfer_cmpr(l, qgroup_info, qgroup->rfer_cmpr); + btrfs_set_qgroup_info_excl(l, qgroup_info, qgroup->excl); + btrfs_set_qgroup_info_excl_cmpr(l, qgroup_info, qgroup->excl_cmpr); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +@@ -903,7 +886,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) + { + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_root *quota_root = fs_info->quota_root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct extent_buffer *l; + struct btrfs_qgroup_status_item *ptr; +@@ -923,7 +906,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) + ret = -ENOENT; + + if (ret) +- goto out; ++ return ret; + + l = path->nodes[0]; + slot = path->slots[0]; +@@ -933,8 +916,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) + btrfs_set_qgroup_status_generation(l, ptr, trans->transid); + btrfs_set_qgroup_status_rescan(l, ptr, + fs_info->qgroup_rescan_progress.objectid); +-out: +- btrfs_free_path(path); ++ + return ret; + } + +@@ -944,7 +926,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) + static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root) + { +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct extent_buffer *leaf = NULL; + int ret; +@@ -961,7 +943,7 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, + while (1) { + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) +- goto out; ++ return ret; + leaf = path->nodes[0]; + nr = btrfs_header_nritems(leaf); + if (!nr) +@@ -974,14 +956,12 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, + path->slots[0] = 0; + ret = btrfs_del_items(trans, root, path, 0, nr); + if (ret) +- goto out; ++ return ret; + + btrfs_release_path(path); + } +- ret = 0; +-out: +- btrfs_free_path(path); +- return ret; ++ ++ return 0; + } + + int btrfs_quota_enable(struct btrfs_fs_info *fs_info, +@@ -1712,8 +1692,7 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) + static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) + { + struct btrfs_key key; +- struct btrfs_path *path; +- int ret; ++ BTRFS_PATH_AUTO_FREE(path); + + /* + * Squota would never be inconsistent, but there can still be case +@@ -1746,13 +1725,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + if (!path) + return -ENOMEM; + +- ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); +- btrfs_free_path(path); + /* + * The @ret from btrfs_find_root() exactly matches our definition for + * the return value, thus can be returned directly. + */ +- return ret; ++ return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); + } + + int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) +@@ -2301,7 +2278,7 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, + bool trace_leaf) + { + struct btrfs_key key; +- struct btrfs_path *src_path; ++ BTRFS_PATH_AUTO_FREE(src_path); + struct btrfs_fs_info *fs_info = trans->fs_info; + u32 nodesize = fs_info->nodesize; + int cur_level = root_level; +@@ -2313,10 +2290,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, + return -EINVAL; + + src_path = btrfs_alloc_path(); +- if (!src_path) { +- ret = -ENOMEM; +- goto out; +- } ++ if (!src_path) ++ return -ENOMEM; + + if (dst_level) + btrfs_node_key_to_cpu(dst_path->nodes[dst_level], &key, 0); +@@ -2342,10 +2317,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, + parent_slot = src_path->slots[cur_level + 1]; + + eb = btrfs_read_node_slot(eb, parent_slot); +- if (IS_ERR(eb)) { +- ret = PTR_ERR(eb); +- goto out; +- } ++ if (IS_ERR(eb)) ++ return PTR_ERR(eb); + + src_path->nodes[cur_level] = eb; + +@@ -2366,10 +2339,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, + &src_key, src_path->slots[cur_level]); + } + /* Content mismatch, something went wrong */ +- if (btrfs_comp_cpu_keys(&dst_key, &src_key)) { +- ret = -ENOENT; +- goto out; +- } ++ if (btrfs_comp_cpu_keys(&dst_key, &src_key)) ++ return -ENOENT; + cur_level--; + } + +@@ -2380,21 +2351,20 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, + ret = btrfs_qgroup_trace_extent(trans, src_path->nodes[dst_level]->start, + nodesize); + if (ret < 0) +- goto out; ++ return ret; + ret = btrfs_qgroup_trace_extent(trans, dst_path->nodes[dst_level]->start, + nodesize); + if (ret < 0) +- goto out; ++ return ret; + + /* Record leaf file extents */ + if (dst_level == 0 && trace_leaf) { + ret = btrfs_qgroup_trace_leaf_items(trans, src_path->nodes[0]); + if (ret < 0) +- goto out; ++ return ret; + ret = btrfs_qgroup_trace_leaf_items(trans, dst_path->nodes[0]); + } +-out: +- btrfs_free_path(src_path); ++ + return ret; + } + +@@ -2595,7 +2565,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, + int level; + u8 drop_subptree_thres; + struct extent_buffer *eb = root_eb; +- struct btrfs_path *path = NULL; ++ BTRFS_PATH_AUTO_FREE(path); + + ASSERT(0 <= root_level && root_level < BTRFS_MAX_LEVEL); + ASSERT(root_eb != NULL); +@@ -2628,12 +2598,12 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, + + ret = btrfs_read_extent_buffer(root_eb, &check); + if (ret) +- goto out; ++ return ret; + } + + if (root_level == 0) { + ret = btrfs_qgroup_trace_leaf_items(trans, root_eb); +- goto out; ++ return ret; + } + + path = btrfs_alloc_path(); +@@ -2669,10 +2639,8 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, + child_bytenr = btrfs_node_blockptr(eb, parent_slot); + + eb = btrfs_read_node_slot(eb, parent_slot); +- if (IS_ERR(eb)) { +- ret = PTR_ERR(eb); +- goto out; +- } ++ if (IS_ERR(eb)) ++ return PTR_ERR(eb); + + path->nodes[level] = eb; + path->slots[level] = 0; +@@ -2683,14 +2651,14 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, + ret = btrfs_qgroup_trace_extent(trans, child_bytenr, + fs_info->nodesize); + if (ret) +- goto out; ++ return ret; + } + + if (level == 0) { + ret = btrfs_qgroup_trace_leaf_items(trans, + path->nodes[level]); + if (ret) +- goto out; ++ return ret; + + /* Nonzero return here means we completed our search */ + ret = adjust_slots_upwards(path, root_level); +@@ -2704,11 +2672,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, + level--; + } + +- ret = 0; +-out: +- btrfs_free_path(path); +- +- return ret; ++ return 0; + } + + static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup) +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index c40944ca7b948..f1bfe97beacf1 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -805,17 +805,15 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, + struct btrfs_root_ref *root_ref; + struct btrfs_inode_ref *inode_ref; + struct btrfs_key key; +- struct btrfs_path *path = NULL; ++ BTRFS_PATH_AUTO_FREE(path); + char *name = NULL, *ptr; + u64 dirid; + int len; + int ret; + + path = btrfs_alloc_path(); +- if (!path) { +- ret = -ENOMEM; +- goto err; +- } ++ if (!path) ++ return ERR_PTR(-ENOMEM); + + name = kmalloc(PATH_MAX, GFP_KERNEL); + if (!name) { +@@ -903,7 +901,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, + fs_root = NULL; + } + +- btrfs_free_path(path); + if (ptr == name + PATH_MAX - 1) { + name[0] = '/'; + name[1] = '\0'; +@@ -914,7 +911,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, + + err: + btrfs_put_root(fs_root); +- btrfs_free_path(path); + kfree(name); + return ERR_PTR(ret); + } +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index 4fd9a417fc5f7..a0e12b4fb9561 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -2647,7 +2647,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc, + int ret = 0; + struct btrfs_key dir_key; + struct btrfs_key found_key; +- struct btrfs_path *log_path; ++ BTRFS_PATH_AUTO_FREE(log_path); + struct btrfs_inode *dir; + + dir_key.objectid = dirid; +@@ -2664,7 +2664,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc, + * we replay the deletes before we copy in the inode item from the log. + */ + if (IS_ERR(dir)) { +- btrfs_free_path(log_path); + ret = PTR_ERR(dir); + if (ret == -ENOENT) + ret = 0; +@@ -2744,7 +2743,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc, + ret = 0; + out: + btrfs_release_path(wc->subvol_path); +- btrfs_free_path(log_path); + iput(&dir->vfs_inode); + return ret; + } +diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c +index ef9f24076ccae..630fb5885692b 100644 +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -4205,7 +4205,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) + struct btrfs_root *chunk_root = fs_info->chunk_root; + u64 chunk_type; + struct btrfs_chunk *chunk; +- struct btrfs_path *path = NULL; ++ BTRFS_PATH_AUTO_FREE(path); + struct btrfs_key key; + struct btrfs_key found_key; + struct extent_buffer *leaf; +@@ -4382,7 +4382,6 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) + goto again; + } + error: +- btrfs_free_path(path); + if (enospc_errors) { + btrfs_info(fs_info, "%d enospc errors during balance", + enospc_errors); +diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c +index 79fb1614bd0c3..b6f01d6c79e7f 100644 +--- a/fs/btrfs/xattr.c ++++ b/fs/btrfs/xattr.c +@@ -85,7 +85,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, + { + struct btrfs_dir_item *di = NULL; + struct btrfs_root *root = BTRFS_I(inode)->root; +- struct btrfs_path *path; ++ BTRFS_PATH_AUTO_FREE(path); + size_t name_len = strlen(name); + int ret = 0; + +@@ -212,7 +212,6 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, + */ + } + out: +- btrfs_free_path(path); + if (!ret) { + set_bit(BTRFS_INODE_COPY_EVERYTHING, + &BTRFS_I(inode)->runtime_flags); +-- +2.53.0 + diff --git a/queue-6.18/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch b/queue-6.18/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch new file mode 100644 index 0000000000..1ee02a211e --- /dev/null +++ b/queue-6.18/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch @@ -0,0 +1,75 @@ +From dde3819e817ce87b5c5b2228da0aa54274197011 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:58:56 +0100 +Subject: btrfs: tracepoints: fix sleep while in atomic context in + btrfs_sync_file() + +From: Filipe Manana + +[ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ] + +The trace event btrfs_sync_file() is called in an atomic context (all trace +events are) and its call to dput(), which is needed due to the call to +dget_parent(), can sleep, triggering a kernel splat. + +This can be reproduced by enabling the trace event and running btrfs/056 +from fstests for example. The splat shown in dmesg is the following: + + [53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 + [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io + [53.988] preempt_count: 2, expected: 0 + [53.967] RCU nest depth: 0, expected: 0 + [53.943] Preemption disabled at: + [53.944] [<0000000000000000>] 0x0 + [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) + [54.070] Tainted: [W]=WARN + [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 + [54.072] Call Trace: + [54.074] + [54.076] dump_stack_lvl+0x56/0x80 + [54.079] __might_resched.cold+0xd6/0x10f + [54.072] dput.part.0+0x24/0x110 + [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] + [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] + [54.087] ? __handle_mm_fault+0x8ae/0xed0 + [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] + [54.091] vfs_write+0x21f/0x450 + [54.094] __x64_sys_pwrite64+0x8d/0xc0 + [54.096] ? do_user_addr_fault+0x20c/0x670 + [54.099] do_syscall_64+0x60/0xf20 + [54.092] ? clear_bhb_loop+0x60/0xb0 + [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e + +So stop using dget_parent() and dput() and access the parent dentry +directly as dentry->d_parent. This is also what ext4 is doing in +its equivalent trace event ext4_sync_file_enter(). + +Fixes: a85b46db143f ("btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()") +Reviewed-by: Boris Burkov +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + include/trace/events/btrfs.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h +index 0864700f76e0a..fa090a455037a 100644 +--- a/include/trace/events/btrfs.h ++++ b/include/trace/events/btrfs.h +@@ -771,10 +771,8 @@ TRACE_EVENT(btrfs_sync_file, + TP_fast_assign( + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); +- struct dentry *parent = dget_parent(dentry); +- struct inode *parent_inode = d_inode(parent); ++ struct inode *parent_inode = d_inode(dentry->d_parent); + +- dput(parent); + TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); + __entry->ino = btrfs_ino(BTRFS_I(inode)); + __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); +-- +2.53.0 + diff --git a/queue-6.18/btrfs-use-the-key-format-macros-when-printing-keys.patch b/queue-6.18/btrfs-use-the-key-format-macros-when-printing-keys.patch new file mode 100644 index 0000000000..6b93abcdfb --- /dev/null +++ b/queue-6.18/btrfs-use-the-key-format-macros-when-printing-keys.patch @@ -0,0 +1,421 @@ +From 01214121637660aca570c4e4fb0ea2d3073dbf9e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Oct 2025 13:16:26 +0100 +Subject: btrfs: use the key format macros when printing keys + +From: Filipe Manana + +[ Upstream commit af1e800c0244a04f5eb0993745c23d974f262628 ] + +Change all locations that print a key to use the new macros to print +them in order to ensure a consistent style and avoid repetitive code. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") +Signed-off-by: Sasha Levin +--- + fs/btrfs/backref.c | 11 +++++------ + fs/btrfs/ctree.c | 17 +++++++---------- + fs/btrfs/extent-tree.c | 14 +++++++------- + fs/btrfs/inode.c | 4 ++-- + fs/btrfs/print-tree.c | 14 ++++++-------- + fs/btrfs/qgroup.c | 6 ++---- + fs/btrfs/relocation.c | 4 ++-- + fs/btrfs/root-tree.c | 4 ++-- + fs/btrfs/send.c | 10 ++++------ + fs/btrfs/tree-checker.c | 21 +++++++++------------ + fs/btrfs/tree-log.c | 38 ++++++++++++++++++-------------------- + 11 files changed, 64 insertions(+), 79 deletions(-) + +diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c +index 2ab550a1e715a..e050d0938dc45 100644 +--- a/fs/btrfs/backref.c ++++ b/fs/btrfs/backref.c +@@ -666,10 +666,9 @@ static int resolve_indirect_ref(struct btrfs_backref_walk_ctx *ctx, + ret = btrfs_search_old_slot(root, &search_key, path, ctx->time_seq); + + btrfs_debug(ctx->fs_info, +- "search slot in root %llu (level %d, ref count %d) returned %d for key (%llu %u %llu)", +- ref->root_id, level, ref->count, ret, +- ref->key_for_search.objectid, ref->key_for_search.type, +- ref->key_for_search.offset); ++"search slot in root %llu (level %d, ref count %d) returned %d for key " BTRFS_KEY_FMT, ++ ref->root_id, level, ref->count, ret, ++ BTRFS_KEY_FMT_VALUE(&ref->key_for_search)); + if (ret < 0) + goto out; + +@@ -3323,9 +3322,9 @@ static int handle_indirect_tree_backref(struct btrfs_trans_handle *trans, + eb = path->nodes[level]; + if (btrfs_node_blockptr(eb, path->slots[level]) != cur->bytenr) { + btrfs_err(fs_info, +-"couldn't find block (%llu) (level %d) in tree (%llu) with key (%llu %u %llu)", ++"couldn't find block (%llu) (level %d) in tree (%llu) with key " BTRFS_KEY_FMT, + cur->bytenr, level - 1, btrfs_root_id(root), +- tree_key->objectid, tree_key->type, tree_key->offset); ++ BTRFS_KEY_FMT_VALUE(tree_key)); + btrfs_put_root(root); + ret = -ENOENT; + goto out; +diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c +index 6e053caa6e101..27e2adc2ee717 100644 +--- a/fs/btrfs/ctree.c ++++ b/fs/btrfs/ctree.c +@@ -2599,12 +2599,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, + if (unlikely(btrfs_comp_keys(&disk_key, new_key) >= 0)) { + btrfs_print_leaf(eb); + btrfs_crit(fs_info, +- "slot %u key (%llu %u %llu) new key (%llu %u %llu)", ++ "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT, + slot, btrfs_disk_key_objectid(&disk_key), + btrfs_disk_key_type(&disk_key), + btrfs_disk_key_offset(&disk_key), +- new_key->objectid, new_key->type, +- new_key->offset); ++ BTRFS_KEY_FMT_VALUE(new_key)); + BUG(); + } + } +@@ -2613,12 +2612,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, + if (unlikely(btrfs_comp_keys(&disk_key, new_key) <= 0)) { + btrfs_print_leaf(eb); + btrfs_crit(fs_info, +- "slot %u key (%llu %u %llu) new key (%llu %u %llu)", ++ "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT, + slot, btrfs_disk_key_objectid(&disk_key), + btrfs_disk_key_type(&disk_key), + btrfs_disk_key_offset(&disk_key), +- new_key->objectid, new_key->type, +- new_key->offset); ++ BTRFS_KEY_FMT_VALUE(new_key)); + BUG(); + } + } +@@ -2677,10 +2675,9 @@ static bool check_sibling_keys(const struct extent_buffer *left, + btrfs_crit(left->fs_info, "right extent buffer:"); + btrfs_print_tree(right, false); + btrfs_crit(left->fs_info, +-"bad key order, sibling blocks, left last (%llu %u %llu) right first (%llu %u %llu)", +- left_last.objectid, left_last.type, +- left_last.offset, right_first.objectid, +- right_first.type, right_first.offset); ++"bad key order, sibling blocks, left last " BTRFS_KEY_FMT " right first " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&left_last), ++ BTRFS_KEY_FMT_VALUE(&right_first)); + return true; + } + return false; +diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c +index 863b45092a190..e40835fe4bde6 100644 +--- a/fs/btrfs/extent-tree.c ++++ b/fs/btrfs/extent-tree.c +@@ -165,8 +165,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, + if (unlikely(num_refs == 0)) { + ret = -EUCLEAN; + btrfs_err(fs_info, +- "unexpected zero reference count for extent item (%llu %u %llu)", +- key.objectid, key.type, key.offset); ++ "unexpected zero reference count for extent item " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&key)); + btrfs_abort_transaction(trans, ret); + return ret; + } +@@ -597,8 +597,8 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans, + num_refs = btrfs_shared_data_ref_count(leaf, ref2); + } else { + btrfs_err(trans->fs_info, +- "unrecognized backref key (%llu %u %llu)", +- key.objectid, key.type, key.offset); ++ "unrecognized backref key " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&key)); + btrfs_abort_transaction(trans, -EUCLEAN); + return -EUCLEAN; + } +@@ -3324,9 +3324,9 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, + if (iref) { + if (unlikely(path->slots[0] != extent_slot)) { + abort_and_dump(trans, path, +-"invalid iref, extent item key (%llu %u %llu) slot %u doesn't have wanted iref", +- key.objectid, key.type, +- key.offset, path->slots[0]); ++"invalid iref, extent item key " BTRFS_KEY_FMT " slot %u doesn't have wanted iref", ++ BTRFS_KEY_FMT_VALUE(&key), ++ path->slots[0]); + ret = -EUCLEAN; + goto out; + } +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 2c361e0691fc5..dcc0e53782c61 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -5668,9 +5668,9 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, + location->type != BTRFS_ROOT_ITEM_KEY)) { + ret = -EUCLEAN; + btrfs_warn(root->fs_info, +-"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))", ++"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location " BTRFS_KEY_FMT ")", + __func__, fname.disk_name.name, btrfs_ino(dir), +- location->objectid, location->type, location->offset); ++ BTRFS_KEY_FMT_VALUE(location)); + } + if (!ret) + *type = btrfs_dir_ftype(path->nodes[0], di); +diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c +index 62b993fae54ff..06edc5cdb00d3 100644 +--- a/fs/btrfs/print-tree.c ++++ b/fs/btrfs/print-tree.c +@@ -131,7 +131,7 @@ static void print_extent_item(const struct extent_buffer *eb, int slot, int type + struct btrfs_tree_block_info *info; + info = (struct btrfs_tree_block_info *)(ei + 1); + btrfs_tree_block_key(eb, info, &key); +- pr_info("\t\ttree block key (%llu %u %llu) level %d\n", ++ pr_info("\t\ttree block key " BTRFS_KEY_FMT " level %d\n", + btrfs_disk_key_objectid(&key), key.type, + btrfs_disk_key_offset(&key), + btrfs_tree_block_level(eb, info)); +@@ -277,9 +277,8 @@ static void print_dir_item(const struct extent_buffer *eb, int i) + struct btrfs_key location; + + btrfs_dir_item_key_to_cpu(eb, di, &location); +- pr_info("\t\tlocation key (%llu %u %llu) type %d\n", +- location.objectid, location.type, location.offset, +- btrfs_dir_ftype(eb, di)); ++ pr_info("\t\tlocation key " BTRFS_KEY_FMT " type %d\n", ++ BTRFS_KEY_FMT_VALUE(&location), btrfs_dir_ftype(eb, di)); + pr_info("\t\ttransid %llu data_len %u name_len %u\n", + btrfs_dir_transid(eb, di), data_len, name_len); + di = (struct btrfs_dir_item *)((char *)di + len); +@@ -598,10 +597,9 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow) + print_eb_refs_lock(c); + for (i = 0; i < nr; i++) { + btrfs_node_key_to_cpu(c, &key, i); +- pr_info("\tkey %d (%llu %u %llu) block %llu gen %llu\n", +- i, key.objectid, key.type, key.offset, +- btrfs_node_blockptr(c, i), +- btrfs_node_ptr_generation(c, i)); ++ pr_info("\tkey %d " BTRFS_KEY_FMT " block %llu gen %llu\n", ++ i, BTRFS_KEY_FMT_VALUE(&key), btrfs_node_blockptr(c, i), ++ btrfs_node_ptr_generation(c, i)); + } + if (!follow) + return; +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 302bb3ecf39a3..1f83af14568ca 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -3734,10 +3734,8 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, + path, 1, 0); + + btrfs_debug(fs_info, +- "current progress key (%llu %u %llu), search_slot ret %d", +- fs_info->qgroup_rescan_progress.objectid, +- fs_info->qgroup_rescan_progress.type, +- fs_info->qgroup_rescan_progress.offset, ret); ++ "current progress key " BTRFS_KEY_FMT ", search_slot ret %d", ++ BTRFS_KEY_FMT_VALUE(&fs_info->qgroup_rescan_progress), ret); + + if (ret) { + /* +diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c +index 0765e06d00b80..fc76013b1a3e0 100644 +--- a/fs/btrfs/relocation.c ++++ b/fs/btrfs/relocation.c +@@ -615,8 +615,8 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, + + btrfs_disk_key_to_cpu(&cpu_key, &root->root_item.drop_progress); + btrfs_err(fs_info, +- "cannot relocate partially dropped subvolume %llu, drop progress key (%llu %u %llu)", +- objectid, cpu_key.objectid, cpu_key.type, cpu_key.offset); ++ "cannot relocate partially dropped subvolume %llu, drop progress key " BTRFS_KEY_FMT, ++ objectid, BTRFS_KEY_FMT_VALUE(&cpu_key)); + ret = -EUCLEAN; + goto fail; + } +diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c +index d07eab70f759d..6a7e297ab0a7a 100644 +--- a/fs/btrfs/root-tree.c ++++ b/fs/btrfs/root-tree.c +@@ -147,8 +147,8 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root + + if (unlikely(ret > 0)) { + btrfs_crit(fs_info, +- "unable to find root key (%llu %u %llu) in tree %llu", +- key->objectid, key->type, key->offset, btrfs_root_id(root)); ++ "unable to find root key " BTRFS_KEY_FMT " in tree %llu", ++ BTRFS_KEY_FMT_VALUE(key), btrfs_root_id(root)); + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + return ret; +diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c +index 9012ce7a742f4..04473387ee8bf 100644 +--- a/fs/btrfs/send.c ++++ b/fs/btrfs/send.c +@@ -1053,10 +1053,8 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path, + } + if (unlikely(start < p->buf)) { + btrfs_err(root->fs_info, +- "send: path ref buffer underflow for key (%llu %u %llu)", +- found_key->objectid, +- found_key->type, +- found_key->offset); ++ "send: path ref buffer underflow for key " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(found_key)); + ret = -EINVAL; + goto out; + } +@@ -7276,8 +7274,8 @@ static int search_key_again(const struct send_ctx *sctx, + if (unlikely(ret > 0)) { + btrfs_print_tree(path->nodes[path->lowest_level], false); + btrfs_err(root->fs_info, +-"send: key (%llu %u %llu) not found in %s root %llu, lowest_level %d, slot %d", +- key->objectid, key->type, key->offset, ++"send: key " BTRFS_KEY_FMT" not found in %s root %llu, lowest_level %d, slot %d", ++ BTRFS_KEY_FMT_VALUE(key), + (root == sctx->parent_root ? "parent" : "send"), + btrfs_root_id(root), path->lowest_level, + path->slots[path->lowest_level]); +diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c +index 33a45737c4cf4..db7402836340a 100644 +--- a/fs/btrfs/tree-checker.c ++++ b/fs/btrfs/tree-checker.c +@@ -1635,10 +1635,9 @@ static int check_extent_item(struct extent_buffer *leaf, + + if (unlikely(prev_end > key->objectid)) { + extent_err(leaf, slot, +- "previous extent [%llu %u %llu] overlaps current extent [%llu %u %llu]", +- prev_key->objectid, prev_key->type, +- prev_key->offset, key->objectid, key->type, +- key->offset); ++ "previous extent " BTRFS_KEY_FMT " overlaps current extent " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(prev_key), ++ BTRFS_KEY_FMT_VALUE(key)); + return -EUCLEAN; + } + } +@@ -2077,10 +2076,9 @@ enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf) + /* Make sure the keys are in the right order */ + if (unlikely(btrfs_comp_cpu_keys(&prev_key, &key) >= 0)) { + generic_err(leaf, slot, +- "bad key order, prev (%llu %u %llu) current (%llu %u %llu)", +- prev_key.objectid, prev_key.type, +- prev_key.offset, key.objectid, key.type, +- key.offset); ++ "bad key order, prev " BTRFS_KEY_FMT " current " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&prev_key), ++ BTRFS_KEY_FMT_VALUE(&key)); + return BTRFS_TREE_BLOCK_BAD_KEY_ORDER; + } + +@@ -2198,10 +2196,9 @@ enum btrfs_tree_block_status __btrfs_check_node(struct extent_buffer *node) + + if (unlikely(btrfs_comp_cpu_keys(&key, &next_key) >= 0)) { + generic_err(node, slot, +- "bad key order, current (%llu %u %llu) next (%llu %u %llu)", +- key.objectid, key.type, key.offset, +- next_key.objectid, next_key.type, +- next_key.offset); ++ "bad key order, current " BTRFS_KEY_FMT " next " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&key), ++ BTRFS_KEY_FMT_VALUE(&next_key)); + return BTRFS_TREE_BLOCK_BAD_KEY_ORDER; + } + } +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index c45c5112c0350..4fd9a417fc5f7 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -199,9 +199,9 @@ static void do_abort_log_replay(struct walk_control *wc, const char *function, + + if (wc->log_leaf) { + btrfs_crit(fs_info, +- "log tree (for root %llu) leaf currently being processed (slot %d key %llu %u %llu):", ++"log tree (for root %llu) leaf currently being processed (slot %d key " BTRFS_KEY_FMT "):", + btrfs_root_id(wc->root), wc->log_slot, +- wc->log_key.objectid, wc->log_key.type, wc->log_key.offset); ++ BTRFS_KEY_FMT_VALUE(&wc->log_key)); + btrfs_print_leaf(wc->log_leaf); + } + +@@ -511,9 +511,9 @@ static int overwrite_item(struct walk_control *wc) + ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0); + if (ret < 0) { + btrfs_abort_log_replay(wc, ret, +- "failed to search subvolume tree for key (%llu %u %llu) root %llu", +- wc->log_key.objectid, wc->log_key.type, +- wc->log_key.offset, btrfs_root_id(root)); ++ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", ++ BTRFS_KEY_FMT_VALUE(&wc->log_key), ++ btrfs_root_id(root)); + return ret; + } + +@@ -619,9 +619,8 @@ static int overwrite_item(struct walk_control *wc) + btrfs_extend_item(trans, wc->subvol_path, item_size - found_size); + } else if (ret) { + btrfs_abort_log_replay(wc, ret, +- "failed to insert item for key (%llu %u %llu)", +- wc->log_key.objectid, wc->log_key.type, +- wc->log_key.offset); ++ "failed to insert item for key " BTRFS_KEY_FMT, ++ BTRFS_KEY_FMT_VALUE(&wc->log_key)); + return ret; + } + dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot); +@@ -830,9 +829,9 @@ static noinline int replay_one_extent(struct walk_control *wc) + &wc->log_key, sizeof(*item)); + if (ret) { + btrfs_abort_log_replay(wc, ret, +- "failed to insert item with key (%llu %u %llu) root %llu", +- wc->log_key.objectid, wc->log_key.type, +- wc->log_key.offset, btrfs_root_id(root)); ++ "failed to insert item with key " BTRFS_KEY_FMT " root %llu", ++ BTRFS_KEY_FMT_VALUE(&wc->log_key), ++ btrfs_root_id(root)); + goto out; + } + dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0], +@@ -1349,9 +1348,9 @@ static inline int __add_inode_ref(struct walk_control *wc, + ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0); + if (ret < 0) { + btrfs_abort_log_replay(wc, ret, +- "failed to search subvolume tree for key (%llu %u %llu) root %llu", +- search_key.objectid, search_key.type, +- search_key.offset, btrfs_root_id(root)); ++ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", ++ BTRFS_KEY_FMT_VALUE(&search_key), ++ btrfs_root_id(root)); + return ret; + } else if (ret == 0) { + /* +@@ -1484,9 +1483,9 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in + } + if (ret < 0) { + btrfs_abort_log_replay(wc, ret, +- "failed to search subvolume tree for key (%llu %u %llu) root %llu", +- wc->log_key.objectid, wc->log_key.type, +- wc->log_key.offset, btrfs_root_id(root)); ++ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", ++ BTRFS_KEY_FMT_VALUE(&wc->log_key), ++ btrfs_root_id(root)); + goto out; + } + +@@ -2701,10 +2700,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc, + wc->subvol_path, 0, 0); + if (ret < 0) { + btrfs_abort_log_replay(wc, ret, +- "failed to search root %llu for key (%llu %u %llu)", ++ "failed to search root %llu for key " BTRFS_KEY_FMT, + btrfs_root_id(root), +- dir_key.objectid, dir_key.type, +- dir_key.offset); ++ BTRFS_KEY_FMT_VALUE(&dir_key)); + goto out; + } + +-- +2.53.0 + diff --git a/queue-6.18/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch b/queue-6.18/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch new file mode 100644 index 0000000000..12669d0a0f --- /dev/null +++ b/queue-6.18/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch @@ -0,0 +1,61 @@ +From aba7f76b8abda9915c39d29359fff28acba86c64 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 11:30:54 +0800 +Subject: cgroup: rstat: relax NMI guard after switch to try_cmpxchg + +From: Cunlong Li + +[ Upstream commit 22572dbcd3486e6c4dced877125bbf50e4e24edf ] + +Commit 36df6e3dbd7e ("cgroup: make css_rstat_updated nmi safe") used +this_cpu_cmpxchg() for the lockless insertion, and therefore required +both ARCH_HAVE_NMI_SAFE_CMPXCHG and ARCH_HAS_NMI_SAFE_THIS_CPU_OPS in +the NMI guard: on archs without the latter, this_cpu_cmpxchg() falls +back to "local_irq_save() + plain cmpxchg", and local_irq_save() +cannot mask NMIs. + +Commit 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in +css_rstat_updated") later replaced this_cpu_cmpxchg() with plain +try_cmpxchg() to fix cross-CPU lockless-list corruption, but left the +NMI guard untouched. After that switch, css_rstat_updated() no longer +performs any this_cpu_*() RMW operations and only relies on the arch +having NMI-safe cmpxchg, so ARCH_HAS_NMI_SAFE_THIS_CPU_OPS is no +longer required in the guard. + +Relax the guard accordingly so that archs which have HAVE_NMI and +ARCH_HAVE_NMI_SAFE_CMPXCHG but not ARCH_HAS_NMI_SAFE_THIS_CPU_OPS +(e.g. sparc, powerpc on PPC64/BOOK3S) can benefit from the existing +CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC path. Without this, the css +is never queued in NMI on those archs, and the atomics staged by +account_{slab,kmem}_nmi_safe() are not drained by flush_nmi_stats(). + +Fixes: 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in css_rstat_updated") +Signed-off-by: Cunlong Li +Signed-off-by: Tejun Heo +Signed-off-by: Sasha Levin +--- + kernel/cgroup/rstat.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c +index ed60ba119c687..de816a43db9f0 100644 +--- a/kernel/cgroup/rstat.c ++++ b/kernel/cgroup/rstat.c +@@ -81,11 +81,10 @@ void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + lockdep_assert_preemption_disabled(); + + /* +- * For archs withnot nmi safe cmpxchg or percpu ops support, ignore +- * the requests from nmi context. ++ * The lockless insertion below relies on NMI-safe cmpxchg; ++ * bail out in NMI on archs that don't provide it. + */ +- if ((!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) || +- !IS_ENABLED(CONFIG_ARCH_HAS_NMI_SAFE_THIS_CPU_OPS)) && in_nmi()) ++ if (!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && in_nmi()) + return; + + rstatc = css_rstat_cpu(css, cpu); +-- +2.53.0 + diff --git a/queue-6.18/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch b/queue-6.18/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch new file mode 100644 index 0000000000..9a050b61d8 --- /dev/null +++ b/queue-6.18/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch @@ -0,0 +1,182 @@ +From 24e5db500b77ffe22dc4e59db7760e3049016342 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 15:08:49 +0800 +Subject: cgroup/rstat: validate cpu before css_rstat_cpu() access + +From: Qing Ming + +[ Upstream commit 8817005efbdfdf5d4e4814cb5dc52b53d12917d7 ] + +css_rstat_updated() is exposed as a BPF kfunc and accepts a +caller-provided cpu argument. The function uses cpu for per-cpu rstat +lookups without checking whether it refers to a valid possible CPU. + +A BPF iter/cgroup program with CAP_BPF and CAP_PERFMON can pass an +invalid cpu value. On an unfixed UBSCAN_BOUNDS test kernel, cpu == +0x7fffffff triggers: + + UBSAN: array-index-out-of-bounds in kernel/cgroup/rstat.c:31:9 + index 2147483647 is out of range for type 'long unsigned int [64]' + Call Trace: + css_rstat_updated + bpf_iter_run_prog + cgroup_iter_seq_show + bpf_seq_read + +Add cpu validation to the BPF-facing css_rstat_updated() kfunc and +move the common implementation to __css_rstat_updated() for in-kernel +callers. + +Fixes: a319185be9f5 ("cgroup: bpf: enable bpf programs to integrate with rstat") +Signed-off-by: Qing Ming +Signed-off-by: Tejun Heo +Signed-off-by: Sasha Levin +--- + block/blk-cgroup.c | 2 +- + include/linux/cgroup.h | 1 + + kernel/cgroup/rstat.c | 30 ++++++++++++++++++++---------- + mm/memcontrol.c | 6 +++--- + 4 files changed, 25 insertions(+), 14 deletions(-) + +diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c +index 9a8d047be580b..f1ea69743c544 100644 +--- a/block/blk-cgroup.c ++++ b/block/blk-cgroup.c +@@ -2241,7 +2241,7 @@ void blk_cgroup_bio_start(struct bio *bio) + } + + u64_stats_update_end_irqrestore(&bis->sync, flags); +- css_rstat_updated(&blkcg->css, cpu); ++ __css_rstat_updated(&blkcg->css, cpu); + put_cpu(); + } + +diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h +index 6ed477338b166..7b2807009155a 100644 +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -713,6 +713,7 @@ static inline void cgroup_path_from_kernfs_id(u64 id, char *buf, size_t buflen) + /* + * cgroup scalable recursive statistics. + */ ++void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu); + void css_rstat_updated(struct cgroup_subsys_state *css, int cpu); + void css_rstat_flush(struct cgroup_subsys_state *css); + +diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c +index 150e5871e66f2..ed60ba119c687 100644 +--- a/kernel/cgroup/rstat.c ++++ b/kernel/cgroup/rstat.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + #include "cgroup-internal.h" + ++#include + #include + + #include +@@ -53,7 +54,7 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) + } + + /** +- * css_rstat_updated - keep track of updated rstat_cpu ++ * __css_rstat_updated - keep track of updated rstat_cpu + * @css: target cgroup subsystem state + * @cpu: cpu on which rstat_cpu was updated + * +@@ -63,20 +64,17 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) + * + * NOTE: if the user needs the guarantee that the updater either add itself in + * the lockless list or the concurrent flusher flushes its updated stats, a +- * memory barrier is needed before the call to css_rstat_updated() i.e. a ++ * memory barrier is needed before the call to __css_rstat_updated() i.e. a + * barrier after updating the per-cpu stats and before calling +- * css_rstat_updated(). ++ * __css_rstat_updated(). + */ +-__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) ++void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + { + struct llist_head *lhead; + struct css_rstat_cpu *rstatc; + struct llist_node *self; + +- /* +- * Since bpf programs can call this function, prevent access to +- * uninitialized rstat pointers. +- */ ++ /* Prevent access to uninitialized rstat pointers. */ + if (!css_uses_rstat(css)) + return; + +@@ -125,6 +123,18 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + llist_add(&rstatc->lnode, lhead); + } + ++/* ++ * BPF-facing wrapper for __css_rstat_updated(). Validate the caller-provided ++ * CPU before passing it to the internal rstat updater. ++ */ ++__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) ++{ ++ if (unlikely(cpu < 0 || cpu >= nr_cpu_ids || !cpu_possible(cpu))) ++ return; ++ ++ __css_rstat_updated(css, cpu); ++} ++ + static void __css_process_update_tree(struct cgroup_subsys_state *css, int cpu) + { + /* put @css and all ancestors on the corresponding updated lists */ +@@ -170,7 +180,7 @@ static void css_process_update_tree(struct cgroup_subsys *ss, int cpu) + * flusher flush the stats updated by the updater who have + * observed that they are already on the list. The + * corresponding barrier pair for this one should be before +- * css_rstat_updated() by the user. ++ * __css_rstat_updated() by the user. + * + * For now, there aren't any such user, so not adding the + * barrier here but if such a use-case arise, please add +@@ -614,7 +624,7 @@ static void cgroup_base_stat_cputime_account_end(struct cgroup *cgrp, + unsigned long flags) + { + u64_stats_update_end_irqrestore(&rstatbc->bsync, flags); +- css_rstat_updated(&cgrp->self, smp_processor_id()); ++ __css_rstat_updated(&cgrp->self, smp_processor_id()); + put_cpu_ptr(rstatbc); + } + +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index 61cf6af26f3c9..4df68e5468add 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -571,7 +571,7 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, + if (!val) + return; + +- css_rstat_updated(&memcg->css, cpu); ++ __css_rstat_updated(&memcg->css, cpu); + statc_pcpu = memcg->vmstats_percpu; + for (; statc_pcpu; statc_pcpu = statc->parent_pcpu) { + statc = this_cpu_ptr(statc_pcpu); +@@ -2525,7 +2525,7 @@ static inline void account_slab_nmi_safe(struct mem_cgroup *memcg, + struct mem_cgroup_per_node *pn = memcg->nodeinfo[pgdat->node_id]; + + /* preemption is disabled in_nmi(). */ +- css_rstat_updated(&memcg->css, smp_processor_id()); ++ __css_rstat_updated(&memcg->css, smp_processor_id()); + if (idx == NR_SLAB_RECLAIMABLE_B) + atomic_add(nr, &pn->slab_reclaimable); + else +@@ -2749,7 +2749,7 @@ static inline void account_kmem_nmi_safe(struct mem_cgroup *memcg, int val) + mod_memcg_state(memcg, MEMCG_KMEM, val); + } else { + /* preemption is disabled in_nmi(). */ +- css_rstat_updated(&memcg->css, smp_processor_id()); ++ __css_rstat_updated(&memcg->css, smp_processor_id()); + atomic_add(val, &memcg->kmem_stat); + } + } +-- +2.53.0 + diff --git a/queue-6.18/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch b/queue-6.18/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch new file mode 100644 index 0000000000..b27c65af93 --- /dev/null +++ b/queue-6.18/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch @@ -0,0 +1,214 @@ +From e6744fc3ab7994cc7ffb25a8dee2a65063c82ebd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 00:05:13 +0100 +Subject: crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks + +From: David Howells + +[ Upstream commit 2b50aceafe6606ea52ed42aadd1b4d44a188aade ] + +Change the krb5 crypto library to provide facilities to precheck the length +of the message about to be decrypted or verified. + +Fix AF_RXRPC to make use of this to validate DATA packets secured with +RxGK. + +Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") +Closes: https://sashiko.dev/#/patchset/20260511160753.607296-1-dhowells%40redhat.com +Signed-off-by: David Howells +cc: Herbert Xu +cc: Simon Horman +cc: Chuck Lever +cc: linux-afs@lists.infradead.org +Reviewed-by: Jeffrey Altman +Tested-by: Marc Dionne +Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + Documentation/crypto/krb5.rst | 17 ++++++++--- + crypto/krb5/krb5_api.c | 54 +++++++++++++++++++++++++++++++---- + include/crypto/krb5.h | 9 ++++-- + include/trace/events/rxrpc.h | 1 + + net/rxrpc/rxgk.c | 15 ++++++++-- + 5 files changed, 81 insertions(+), 15 deletions(-) + +diff --git a/Documentation/crypto/krb5.rst b/Documentation/crypto/krb5.rst +index beffa0133446d..f62e07ac68114 100644 +--- a/Documentation/crypto/krb5.rst ++++ b/Documentation/crypto/krb5.rst +@@ -158,13 +158,22 @@ returned. + When a message has been received, the location and size of the data with the + message can be determined by calling:: + +- void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len); ++ int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len); + + The caller provides the offset and length of the message to the function, which + then alters those values to indicate the region containing the data (plus any +-padding). It is up to the caller to determine how much padding there is. ++padding). It is up to the caller to determine how much padding there is. The ++function returns an error if the length is too small or if the mode is ++unsupported. An additional function:: ++ ++ int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content); ++ ++is provided to just do a basic check that the decrypted/verified message would ++have a sufficient minimum payload. + + Preparation Functions + --------------------- +diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c +index 23026d4206c82..c7ea40f900a77 100644 +--- a/crypto/krb5/krb5_api.c ++++ b/crypto/krb5/krb5_api.c +@@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data); + * Find the offset and size of the data in a secure message so that this + * information can be used in the metadata buffer which will get added to the + * digest by crypto_krb5_verify_mic(). ++ * ++ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if ++ * the mode is unsupported. + */ +-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len) ++int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len) + { + switch (mode) { + case KRB5_CHECKSUM_MODE: ++ if (*_len < krb5->cksum_len) ++ return -EBADMSG; + *_offset += krb5->cksum_len; + *_len -= krb5->cksum_len; +- return; ++ return 0; + case KRB5_ENCRYPT_MODE: ++ if (*_len < krb5->conf_len + krb5->cksum_len) ++ return -EBADMSG; + *_offset += krb5->conf_len; + *_len -= krb5->conf_len + krb5->cksum_len; +- return; ++ return 0; + default: + WARN_ON_ONCE(1); +- return; ++ return -EINVAL; + } + } + EXPORT_SYMBOL(crypto_krb5_where_is_the_data); + ++/** ++ * crypto_krb5_check_data_len - Check a message is big enough ++ * @krb5: The encoding to use. ++ * @mode: Mode of operation. ++ * @len: The length of the secure blob. ++ * @min_content: Minimum length of the content inside the blob. ++ * ++ * Check that a message is large enough to hold whatever bits the encryption ++ * type wants to glue on (nonce, checksum) plus a minimum amount of content. ++ * ++ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if ++ * the mode is unsupported. ++ */ ++int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content) ++{ ++ switch (mode) { ++ case KRB5_CHECKSUM_MODE: ++ if (len < krb5->cksum_len || ++ len - krb5->cksum_len < min_content) ++ return -EBADMSG; ++ return 0; ++ case KRB5_ENCRYPT_MODE: ++ if (len < krb5->conf_len + krb5->cksum_len || ++ len - (krb5->conf_len + krb5->cksum_len) < min_content) ++ return -EBADMSG; ++ return 0; ++ default: ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL(crypto_krb5_check_data_len); ++ + /* + * Prepare the encryption with derived key data. + */ +diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h +index 71dd38f59be1d..aac3ecf88467c 100644 +--- a/include/crypto/krb5.h ++++ b/include/crypto/krb5.h +@@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5, + size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_buffer_size, size_t *_offset); +-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len); ++int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len); ++int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content); + struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, gfp_t gfp); +diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h +index 573f2df3a2c99..704a10de66700 100644 +--- a/include/trace/events/rxrpc.h ++++ b/include/trace/events/rxrpc.h +@@ -71,6 +71,7 @@ + EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \ + EM(rxkad_abort_resp_version, "rxkad-resp-version") \ + /* RxGK security errors */ \ ++ EM(rxgk_abort_1_short_header, "rxgk1-short-hdr") \ + EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \ + EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \ + EM(rxgk_abort_2_short_data, "rxgk2-short-data") \ +diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c +index c39f5066d8e86..04a761c79548a 100644 +--- a/net/rxrpc/rxgk.c ++++ b/net/rxrpc/rxgk.c +@@ -480,8 +480,12 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + + _enter(""); + +- crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, +- &data_offset, &data_len); ++ if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, ++ &data_offset, &data_len) < 0) { ++ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, ++ rxgk_abort_1_short_header); ++ goto put_gk; ++ } + + hdr = kzalloc(sizeof(*hdr), GFP_NOFS); + if (!hdr) +@@ -529,6 +533,13 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + + _enter(""); + ++ if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE, ++ len, sizeof(hdr)) < 0) { ++ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, ++ rxgk_abort_2_short_header); ++ goto error; ++ } ++ + ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); + if (ret < 0) { + if (ret != -ENOMEM) +-- +2.53.0 + diff --git a/queue-6.18/dma-mapping-move-dma_map_resource-sanity-check-into-.patch b/queue-6.18/dma-mapping-move-dma_map_resource-sanity-check-into-.patch new file mode 100644 index 0000000000..0b42192f85 --- /dev/null +++ b/queue-6.18/dma-mapping-move-dma_map_resource-sanity-check-into-.patch @@ -0,0 +1,86 @@ +From df6725b77b43e5765ca553d214ab926e73a38531 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 15:22:09 +0800 +Subject: dma-mapping: move dma_map_resource() sanity check into debug code + +From: Jianpeng Chang + +[ Upstream commit af0c3f05866237f7592219bfe05387bc3bfc99b5 ] + +dma_map_resource() uses pfn_valid() to ensure the range is not RAM. +However, pfn_valid() only checks for availability of the memory map for +a PFN but it does not ensure that the PFN is actually backed by RAM. On +ARM64 with SPARSEMEM (128MB section granularity), MMIO addresses that +share a section with RAM will falsely trigger the WARN_ON_ONCE and cause +dma_map_resource() to return DMA_MAPPING_ERROR. + +This causes a WARNING on Raspberry Pi 4 during spi_bcm2835 probe because +the SPI FIFO register (0xfe204004) falls in the same sparsemem section +as the end of RAM (0xf8000000-0xfbffffff), both in section 31 +(0xf8000000-0xffffffff). + +Move the sanity check from dma_map_resource() into debug_dma_map_phys() +and replace the unreliable pfn_valid() with pfn_valid() && +!PageReserved(), which correctly identifies actual usable RAM without +false positives for MMIO regions that happen to have struct pages. + +Since dma_map_resource() is dma_map_phys(DMA_ATTR_MMIO), the check +applies equally to both APIs. Any non-reserved page represents kernel +memory to a sufficient degree that using DMA_ATTR_MMIO on it is almost +certainly wrong and risks breaking coherency on non-coherent platforms. +ZONE_DEVICE pages used for PCI P2P DMA (MEMORY_DEVICE_PCI_P2PDMA) have +PageReserved set, so they will not trigger a false positive. + +The check no longer blocks the mapping and uses err_printk() to +integrate with dma-debug filtering. + +Fixes: f7326196a781 ("dma-mapping: export new dma_*map_phys() interface") +Reviewed-by: Robin Murphy +Signed-off-by: Jianpeng Chang +Reviewed-by: Leon Romanovsky +Signed-off-by: Marek Szyprowski +Link: https://lore.kernel.org/r/20260513072209.1486986-1-jianpeng.chang.cn@windriver.com +Signed-off-by: Sasha Levin +--- + kernel/dma/debug.c | 9 ++++++++- + kernel/dma/mapping.c | 4 ---- + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c +index 21db331185911..fa4aac3339172 100644 +--- a/kernel/dma/debug.c ++++ b/kernel/dma/debug.c +@@ -1250,7 +1250,14 @@ void debug_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, + entry->direction = direction; + entry->map_err_type = MAP_ERR_NOT_CHECKED; + +- if (!(attrs & DMA_ATTR_MMIO)) { ++ if (attrs & DMA_ATTR_MMIO) { ++ unsigned long pfn = PHYS_PFN(phys); ++ ++ if (pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn))) ++ err_printk(dev, entry, ++ "dma_map_resource called for RAM address %pa\n", ++ &phys); ++ } else { + check_for_stack(dev, phys); + + if (!PhysHighMem(phys)) +diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c +index fe7472f13b106..35e4556b95566 100644 +--- a/kernel/dma/mapping.c ++++ b/kernel/dma/mapping.c +@@ -366,10 +366,6 @@ EXPORT_SYMBOL(dma_unmap_sg_attrs); + dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, + size_t size, enum dma_data_direction dir, unsigned long attrs) + { +- if (IS_ENABLED(CONFIG_DMA_API_DEBUG) && +- WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr)))) +- return DMA_MAPPING_ERROR; +- + return dma_map_phys(dev, phys_addr, size, dir, attrs | DMA_ATTR_MMIO); + } + EXPORT_SYMBOL(dma_map_resource); +-- +2.53.0 + diff --git a/queue-6.18/documentation-intel_pstate-fix-description-of-asymme.patch b/queue-6.18/documentation-intel_pstate-fix-description-of-asymme.patch new file mode 100644 index 0000000000..ecc13dd49b --- /dev/null +++ b/queue-6.18/documentation-intel_pstate-fix-description-of-asymme.patch @@ -0,0 +1,60 @@ +From d7ce4bec4ca571120f84ca62bc2ce85211a5f87f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 14:41:13 -0700 +Subject: Documentation: intel_pstate: Fix description of asymmetric packing + with SMT + +From: Ricardo Neri + +[ Upstream commit ee047fc7a2da90554410128195058c409a391d43 ] + +Patchset [1], including commits + + 046a5a95c3b0 ("x86/sched/itmt: Give all SMT siblings of a core the same priority") + 995998ebdebd ("x86/sched: Remove SD_ASYM_PACKING from the SMT domain flags") + +overhauled asym_packing handling in the scheduler on x86 hybrid +processors with SMT. It removed SD_ASYM_PACKING from the x86 SMT +scheduling domain and made all SMT siblings of a core share the same +priority. As a result, asym_packing operates only across physical +cores, spreading tasks among them and only using idle SMT siblings +once all physical cores are busy. + +Fix the documentation to reflect this behavior. + +Fixes: f20af84c29b2 ("cpufreq: intel_pstate: Document hybrid processor support") +Link: https://lore.kernel.org/r/20230406203148.19182-1-ricardo.neri-calderon@linux.intel.com [1] +Signed-off-by: Ricardo Neri +[ rjw: Changelog edits ] +Link: https://patch.msgid.link/20260424-rneri-fix-intel-pstate-doc-smt-asym-packing-v1-1-317bf7d5c362@linux.intel.com +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +--- + Documentation/admin-guide/pm/intel_pstate.rst | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst +index 26e702c7016e5..66287f8d645f0 100644 +--- a/Documentation/admin-guide/pm/intel_pstate.rst ++++ b/Documentation/admin-guide/pm/intel_pstate.rst +@@ -348,11 +348,12 @@ HyperThreading (HT) in the context of Intel processors, is enabled on at least + one core, ``intel_pstate`` assigns performance-based priorities to CPUs. Namely, + the priority of a given CPU reflects its highest HWP performance level which + causes the CPU scheduler to generally prefer more performant CPUs, so the less +-performant CPUs are used when the other ones are fully loaded. However, SMT +-siblings (that is, logical CPUs sharing one physical core) are treated in a +-special way such that if one of them is in use, the effective priority of the +-other ones is lowered below the priorities of the CPUs located in the other +-physical cores. ++performant CPUs are used when the other ones are fully loaded. SMT siblings ++(that is, logical CPUs sharing one physical core) are given the same priority. ++The scheduler can pull tasks from lower-priority cores and place them on any ++sibling. Since the scheduler spreads tasks among physical cores, tasks will be ++placed on the SMT siblings of physical cores only after all physical cores are ++busy. + + This approach maximizes performance in the majority of cases, but unfortunately + it also leads to excessive energy usage in some important scenarios, like video +-- +2.53.0 + diff --git a/queue-6.18/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch b/queue-6.18/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch new file mode 100644 index 0000000000..e5843a7b9e --- /dev/null +++ b/queue-6.18/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch @@ -0,0 +1,46 @@ +From 2ee3a0a836047beb7839fd8d3a49ff05d82cd2cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 18:02:15 +0530 +Subject: drm/i915/dp: Fix readback for target_rr in Adaptive Sync SDP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ankit Nautiyal + +[ Upstream commit f87abd0c6604fb6cc31cc86fc7ccc6a576924352 ] + +Correct the bit-shift logic to properly readback the 10 bit target_rr from +DB3 and DB4. + +v2: Align the style with readback for vtotal. (Ville) + +Fixes: 12ea89291603 ("drm/i915/dp: Add Read/Write support for Adaptive Sync SDP") +Cc: Mitul Golani +Cc: Ankit Nautiyal +Signed-off-by: Ankit Nautiyal +Reviewed-by: Ville Syrjälä +Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com +(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 61f22275403bf..a44fbac1e5e27 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -4899,7 +4899,7 @@ int intel_dp_as_sdp_unpack(struct drm_dp_as_sdp *as_sdp, + as_sdp->length = sdp->sdp_header.HB3 & DP_ADAPTIVE_SYNC_SDP_LENGTH; + as_sdp->mode = sdp->db[0] & DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE; + as_sdp->vtotal = (sdp->db[2] << 8) | sdp->db[1]; +- as_sdp->target_rr = (u64)sdp->db[3] | ((u64)sdp->db[4] & 0x3); ++ as_sdp->target_rr = ((sdp->db[4] & 0x3) << 8) | sdp->db[3]; + as_sdp->target_rr_divider = sdp->db[4] & 0x20 ? true : false; + + return 0; +-- +2.53.0 + diff --git a/queue-6.18/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch b/queue-6.18/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch new file mode 100644 index 0000000000..f41c6c2e3e --- /dev/null +++ b/queue-6.18/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch @@ -0,0 +1,42 @@ +From fc2cecb8e9297b80206d81f98fb4bf61dab48ae3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:59:01 +0200 +Subject: drm/mediatek: mtk_cec: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit 571f00a5fb725984049bd532ee8193cc34ff2994 ] + +The struct 'mtk_cec_driver' is not used outside of the +mtk_cec.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_cec.c:243:24: sparse: warning: symbol +'mtk_cec_driver' was not declared. Should it be static? +``` + +Fixes: 1e914a89ab7e ("drm/mediatek: mtk_cec: Switch to register as module_platform_driver") +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-3-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_cec.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c +index c7be530ca041f..b8ccd6e55bedb 100644 +--- a/drivers/gpu/drm/mediatek/mtk_cec.c ++++ b/drivers/gpu/drm/mediatek/mtk_cec.c +@@ -240,7 +240,7 @@ static const struct of_device_id mtk_cec_of_ids[] = { + }; + MODULE_DEVICE_TABLE(of, mtk_cec_of_ids); + +-struct platform_driver mtk_cec_driver = { ++static struct platform_driver mtk_cec_driver = { + .probe = mtk_cec_probe, + .remove = mtk_cec_remove, + .driver = { +-- +2.53.0 + diff --git a/queue-6.18/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch b/queue-6.18/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch new file mode 100644 index 0000000000..57cfa51beb --- /dev/null +++ b/queue-6.18/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch @@ -0,0 +1,42 @@ +From 2f72b50551dc25001add3321a0b5c7d63d398956 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:59:02 +0200 +Subject: drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit 87ed4e845d5a90bba1a56c0a5c580a13982e8648 ] + +The struct 'mtk_hdmi_ddc_driver' is not used outside of the +mtk_hdmi_ddc.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:331:24: sparse: warning: symbol + 'mtk_hdmi_ddc_driver' was not declared. Should it be static? +``` + +Fixes: c241118b6216 ("drm/mediatek: mtk_hdmi_ddc: Switch to register as module_platform_driver") +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-4-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +index 6358e1af69b49..2acbdb025d893 100644 +--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c ++++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +@@ -328,7 +328,7 @@ static const struct of_device_id mtk_hdmi_ddc_match[] = { + }; + MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match); + +-struct platform_driver mtk_hdmi_ddc_driver = { ++static struct platform_driver mtk_hdmi_ddc_driver = { + .probe = mtk_hdmi_ddc_probe, + .remove = mtk_hdmi_ddc_remove, + .driver = { +-- +2.53.0 + diff --git a/queue-6.18/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch b/queue-6.18/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch new file mode 100644 index 0000000000..cd2511a319 --- /dev/null +++ b/queue-6.18/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch @@ -0,0 +1,55 @@ +From 9cf5cacc77e7a317a7a85d88b978d8a649fd8fd1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 11 Apr 2026 17:59:15 +0300 +Subject: drm/msm/adreno: fix userspace-triggered crash on a2xx-a4xx + +From: Dmitry Baryshkov + +[ Upstream commit 2b4abf879360ea00a9e2b46d2d15dcdbc0687eed ] + +Before a5xx Adreno driver will not try fetching UBWC params (because +those generations didn't support UBWC anyway), however it's still +possible to query UBWC-related params from the userspace, triggering +possible NULL pointer dereference. Check for UBWC config in +adreno_get_param() and return sane defaults if there is none. + +Fixes: a452510aad53 ("drm/msm/adreno: Switch to the common UBWC config struct") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Rob Clark +Patchwork: https://patchwork.freedesktop.org/patch/717778/ +Message-ID: <20260411-adreno-fix-ubwc-v3-1-4983156f3f80@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +index 4b5a4edd07028..056a9e18cd4ac 100644 +--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +@@ -419,15 +419,21 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, + *value = vm->mm_range; + return 0; + case MSM_PARAM_HIGHEST_BANK_BIT: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->highest_bank_bit; + return 0; + case MSM_PARAM_RAYTRACING: + *value = adreno_gpu->has_ray_tracing; + return 0; + case MSM_PARAM_UBWC_SWIZZLE: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->ubwc_swizzle; + return 0; + case MSM_PARAM_MACROTILE_MODE: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->macrotile_mode; + return 0; + case MSM_PARAM_UCHE_TRAP_BASE: +-- +2.53.0 + diff --git a/queue-6.18/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch b/queue-6.18/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch new file mode 100644 index 0000000000..9400578e0d --- /dev/null +++ b/queue-6.18/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch @@ -0,0 +1,53 @@ +From f64fc839550496afb27a272c5460edaa9548df9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 03:24:58 +0300 +Subject: drm/msm/dpu: don't mix devm and drmm functions + +From: Dmitry Baryshkov + +[ Upstream commit c0c70a11365cba7fba25a77463582bcec0f7846e ] + +Mixing devm and drmm functions will result in a use-after-free on msm +driver teardown if userspace keeps a reference on the drm device: +The WB connector data will be destroyed because of the use of +devm_kzalloc()), while the usersoace still can try interacting with the +WB connector (which uses drmm_ functions). + +Change dpu_writeback_init() to use drmm_. + +Fixes: 0b37ac63fc9d ("drm/msm/dpu: use drmm_writeback_connector_init()") +Reported-by: Christophe JAILLET +Closes: https://lore.kernel.org/r/78c764b8-44cf-4db5-88e7-807a85954518@wanadoo.fr +Signed-off-by: Dmitry Baryshkov +Reviewed-by: John.Harrison@Igalia.com +Patchwork: https://patchwork.freedesktop.org/patch/722656/ +Link: https://lore.kernel.org/r/20260505-wb-drop-encoder-v5-1-42567b7c7af2@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +index 7545c0293efbd..6f2370c9dd988 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #include "dpu_writeback.h" + +@@ -125,7 +126,7 @@ int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + struct dpu_wb_connector *dpu_wb_conn; + int rc = 0; + +- dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL); ++ dpu_wb_conn = drmm_kzalloc(dev, sizeof(*dpu_wb_conn), GFP_KERNEL); + if (!dpu_wb_conn) + return -ENOMEM; + +-- +2.53.0 + diff --git a/queue-6.18/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch b/queue-6.18/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch new file mode 100644 index 0000000000..ddd7f0abfb --- /dev/null +++ b/queue-6.18/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch @@ -0,0 +1,50 @@ +From 85b316bcab133ba20c36028ebd44068c55d898a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 20:21:38 +0300 +Subject: drm/msm/dsi: don't dump registers past the mapped region + +From: Dmitry Baryshkov + +[ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] + +On DSI 6G platforms the IO address space is internally adjusted by +io_offset. Later this adjusted address might be used for memory dumping. +However the size that is used for memory dumping isn't adjusted to +account for the io_offset, leading to the potential access to the +unmapped region. Lower ctrl_size by the io_offset value to prevent +access past the mapped area. + + msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) + msm_dsi_host_snapshot+0x4c/0x78 [msm] + msm_dsi_snapshot+0x28/0x50 [msm] + msm_disp_snapshot_capture_state+0x74/0x140 [msm] + msm_disp_snapshot_state_sync+0x60/0x90 [msm] + _msm_disp_snapshot_work+0x30/0x90 [msm] + kthread_worker_fn+0xdc/0x460 + kthread+0x120/0x140 + +Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Patchwork: https://patchwork.freedesktop.org/patch/721747/ +Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c +index 1c0841a1c1013..50474c994d473 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_host.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_host.c +@@ -2003,6 +2003,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) + + /* fixup base address by io offset */ + msm_host->ctrl_base += cfg->io_offset; ++ msm_host->ctrl_size -= cfg->io_offset; + + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, +-- +2.53.0 + diff --git a/queue-6.18/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-6.18/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..c6c4f9491b --- /dev/null +++ b/queue-6.18/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,52 @@ +From 2a8d034e092ac134e7eb545775aff6a3eff2771b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index a188617653e85..82a172f21a3ec 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -677,7 +677,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + WARN_ON(off != 0); + +@@ -686,7 +686,8 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.18/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-6.18/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..2218636a47 --- /dev/null +++ b/queue-6.18/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From 65f83c5574d092230011fa51a7c330f0ec048084 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index 071bcdea80f71..591507db26468 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -9,7 +9,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + *reg = kvzalloc(len_padded, GFP_KERNEL); + if (!*reg) +@@ -48,8 +48,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -58,6 +58,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -67,6 +68,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -186,7 +198,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-6.18/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch b/queue-6.18/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch new file mode 100644 index 0000000000..f58ad7e465 --- /dev/null +++ b/queue-6.18/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch @@ -0,0 +1,56 @@ +From e4f6de776ba73ab31768b571d0508bbf0eaf71c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:41:34 +0000 +Subject: drm/xe/gsc: Fix double-free of managed BO in error path + +From: Shuicheng Lin + +[ Upstream commit d3ded53fab90996e7d94a39049e11962dd066725 ] + +The error path in xe_gsc_init_post_hwconfig() explicitly frees a BO +allocated with xe_managed_bo_create_pin_map() via +xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm +cleanup action registered, this causes a double-free when devm +unwinds during probe failure. + +Remove the explicit free and let devm handle it, consistent with +all other xe_managed_bo_create_pin_map() callers. + +Fixes: 2e5d47fe7839 ("drm/xe/uc: Use managed bo for HuC and GSC objects") +Reviewed-by: Daniele Ceraolo Spurio +Assisted-by: Claude:claude-opus-4.6 +Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gsc.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c +index 8371ec002e4ed..2a496987b8299 100644 +--- a/drivers/gpu/drm/xe/xe_gsc.c ++++ b/drivers/gpu/drm/xe/xe_gsc.c +@@ -487,8 +487,7 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + EXEC_QUEUE_FLAG_PERMANENT, 0); + if (IS_ERR(q)) { + xe_gt_err(gt, "Failed to create queue for GSC submission\n"); +- err = PTR_ERR(q); +- goto out_bo; ++ return PTR_ERR(q); + } + + wq = alloc_ordered_workqueue("gsc-ordered-wq", 0); +@@ -511,8 +510,6 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + + out_q: + xe_exec_queue_put(q); +-out_bo: +- xe_bo_unpin_map_no_vm(bo); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.18/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch b/queue-6.18/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch new file mode 100644 index 0000000000..6bd96805d9 --- /dev/null +++ b/queue-6.18/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch @@ -0,0 +1,55 @@ +From 38e0fa7bd2819c0ab67ffa26a50eeb6b1e7769bf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 20:32:10 +0000 +Subject: drm/xe/oa: Fix exec_queue leak on width check in stream open + +From: Shuicheng Lin + +[ Upstream commit 4d25342543c01310fc4e0cba7cb17c775e2421e2 ] + +In xe_oa_stream_open_ioctl(), when param.exec_q->width > 1 the +function returns -EOPNOTSUPP directly, skipping the existing +err_exec_q cleanup path. The exec_queue reference obtained by +xe_exec_queue_lookup() is leaked. + +The exec queue holds a reference on the xe_file, which is only +dropped during queue teardown. The leaked lookup ref is not on +the file's exec_queue xarray, so file close cannot release it. +This keeps both the exec queue and the file private state pinned +indefinitely. + +Jump to err_exec_q instead of returning directly so the reference +is released. + +Fixes: f0ed39830e60 ("xe/oa: Fix query mode of operation for OAR/OAC") +Assisted-by: Claude:claude-opus-4.6 +Reviewed-by: Ashutosh Dixit +Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c +index 98bfb127eafca..7d04591e297a8 100644 +--- a/drivers/gpu/drm/xe/xe_oa.c ++++ b/drivers/gpu/drm/xe/xe_oa.c +@@ -2043,8 +2043,10 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f + if (XE_IOCTL_DBG(oa->xe, !param.exec_q)) + return -ENOENT; + +- if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) +- return -EOPNOTSUPP; ++ if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) { ++ ret = -EOPNOTSUPP; ++ goto err_exec_q; ++ } + } + + /* +-- +2.53.0 + diff --git a/queue-6.18/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch b/queue-6.18/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch new file mode 100644 index 0000000000..a041ac4751 --- /dev/null +++ b/queue-6.18/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch @@ -0,0 +1,76 @@ +From 3a56cc76042fa6db6fcb9443529447244883ec11 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 23:19:18 +0530 +Subject: drm/xe/pf: Fix CFI failure in debugfs access + +From: Mohanram Meenakshisundaram + +[ Upstream commit 96bf49b526e2d03a2b7f6e861925a08f46ed0d28 ] + +Reading debugfs file (/sys/kernel/debug/dri/0/gt*/pf/adverse_events) +with CFI (Control Flow Integrity) enabled, the kernel panics at +xe_gt_debugfs_simple_show+0x82/0xc0. + +xe_gt_debugfs_simple_show() declare a function pointer expecting int +return type, but xe_gt_sriov_pf_monitor_print_events() is void return +type, leading to CFI failure and kernel panic. + +[507620.973657] CFI failure at xe_gt_debugfs_simple_show+0x82/0xc0 [xe] +(target: xe_gt_sriov_pf_monitor_print_events+0x0/0x130 [xe]; expected +type: 0xd72c7139) + +Fix xe_gt_sriov_pf_monitor_print_events() function by updating to return +an int type. + +Fixes: 1c99d3d3edab ("drm/xe/pf: Expose PF monitor details via debugfs") +Signed-off-by: Mohanram Meenakshisundaram +Reviewed-by: Michal Wajdeczko +Signed-off-by: Michal Wajdeczko +Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com +(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c | 6 +++++- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h | 2 +- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +index 7d532bded02a8..a85ba44353789 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +@@ -114,8 +114,10 @@ int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 + * VFs with no events are not printed. + * + * This function can only be called on PF. ++ * ++ * Return: always 0 + */ +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) + { + unsigned int n, total_vfs = xe_gt_sriov_pf_get_totalvfs(gt); + const struct xe_gt_sriov_monitor *data; +@@ -144,4 +146,6 @@ void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p + #undef __format + #undef __value + } ++ ++ return 0; + } +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +index 7ca9351a271b7..0b8f088d3a16a 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +@@ -13,7 +13,7 @@ struct drm_printer; + struct xe_gt; + + void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid); +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); + + #ifdef CONFIG_PCI_IOV + int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len); +-- +2.53.0 + diff --git a/queue-6.18/drm-xe-vf-fix-signature-of-print-functions.patch b/queue-6.18/drm-xe-vf-fix-signature-of-print-functions.patch new file mode 100644 index 0000000000..04cdf05c5e --- /dev/null +++ b/queue-6.18/drm-xe-vf-fix-signature-of-print-functions.patch @@ -0,0 +1,121 @@ +From 2ccca78a5881f118ba72ad91a7402d18c19bc6db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 17:57:26 +0200 +Subject: drm/xe/vf: Fix signature of print functions + +From: Michal Wajdeczko + +[ Upstream commit 9bb2f1d7e6e58b8e434ddc2048c661bf87ccdf2a ] + +We have plugged-in existing VF print functions into our GT debugfs +show helper as-is, but we missed that the helper expects functions +to return int, while they were defined as void. This can lead to +errors being reported when CFI is enabled. + +Fixes: 63d8cb8fe3dd ("drm/xe/vf: Expose SR-IOV VF attributes to GT debugfs") +Signed-off-by: Michal Wajdeczko +Cc: Mohanram Meenakshisundaram +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com +(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_vf.c | 24 ++++++++++++++++++------ + drivers/gpu/drm/xe/xe_gt_sriov_vf.h | 6 +++--- + 2 files changed, 21 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +index 0461d55134874..ca58ef3f9fd39 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +@@ -1030,13 +1030,15 @@ void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val) + } + + /** +- * xe_gt_sriov_vf_print_config - Print VF self config. ++ * xe_gt_sriov_vf_print_config() - Print VF self config. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config; + struct xe_device *xe = gt_to_xe(gt); +@@ -1060,16 +1062,20 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + + drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs); + drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF. ++ * xe_gt_sriov_vf_print_runtime() - Print VF's runtime regs received from PF. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + { + struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs; + unsigned int size = gt->sriov.vf.runtime.num_regs; +@@ -1078,16 +1084,20 @@ void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + + for (; size--; vf_regs++) + drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_version - Print VF ABI versions. ++ * xe_gt_sriov_vf_print_version() - Print VF ABI versions. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_device *xe = gt_to_xe(gt); + struct xe_uc_fw_version *guc_version = >->sriov.vf.guc_version; +@@ -1117,4 +1127,6 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR); + drm_printf(p, "\thandshake:\t%u.%u\n", + pf_version->major, pf_version->minor); ++ ++ return 0; + } +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +index 0af1dc769fe09..7f6c59b1ef7b6 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +@@ -35,8 +35,8 @@ s64 xe_gt_sriov_vf_ggtt_shift(struct xe_gt *gt); + u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg); + void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val); + +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); + + #endif +-- +2.53.0 + diff --git a/queue-6.18/erofs-fix-managed-cache-race-for-unaligned-extents.patch b/queue-6.18/erofs-fix-managed-cache-race-for-unaligned-extents.patch new file mode 100644 index 0000000000..ea0b96fd6b --- /dev/null +++ b/queue-6.18/erofs-fix-managed-cache-race-for-unaligned-extents.patch @@ -0,0 +1,89 @@ +From bd334fb762e43eefc1455a145cddf8eeb0899eb7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 12:34:31 +0800 +Subject: erofs: fix managed cache race for unaligned extents + +From: Gao Xiang + +[ Upstream commit 649932fc3815eda2f24eb4de4b3a5e94886ee0b9 ] + +After unaligned compressed extents were introduced, the following race +could occur: + +[Thread 1] [Thread 2] +(z_erofs_fill_bio_vec) + +... +filemap_add_folio (1) + (z_erofs_bind_cache) + + .. + .. +folio_attach_private (2) + filemap_add_folio (3) again + +Since (1) is executed but (2) hasn't been executed yet, it's possible +that another thread finds the same managed folio in z_erofs_bind_cache() +for a different pcluster and calls filemap_add_folio() again since +folio->private is still Z_EROFS_PREALLOCATED_FOLIO. + +Fix this by explicitly clearing folio->private before making the folio +visible in the managed cache so that another pcluster can simply wait +on the locked managed folio as what we did for other shared cases [1]. + +This only impacts unaligned data compression (`-E48bit` with zstd, +for example). + +[1] Commit 9e2f9d34dd12 ("erofs: handle overlapped pclusters out of + crafted images properly") was originally introduced to handle crafted + overlapped extents, but it addresses unaligned extents as well. + +Fixes: 7361d1e3763b ("erofs: support unaligned encoded data") +Reported-by: Arseniy Krasnov +Closes: https://lore.kernel.org/r/4a2f3801-fac1-42fe-ae75-da315822e088@salutedevices.com +Tested-by: Arseniy Krasnov +Signed-off-by: Gao Xiang +Signed-off-by: Sasha Levin +--- + fs/erofs/zdata.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c +index 71f01f0a07435..33932d56d3a46 100644 +--- a/fs/erofs/zdata.c ++++ b/fs/erofs/zdata.c +@@ -1511,8 +1511,15 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, + DBG_BUGON(z_erofs_is_shortlived_page(bvec->bv_page)); + + folio = page_folio(zbv.page); +- /* For preallocated managed folios, add them to page cache here */ ++ /* ++ * Preallocated folios are added to the managed cache here rather than ++ * in z_erofs_bind_cache() in order to keep these folios locked in ++ * increasing (physical) address order. ++ * Clear folio->private before these folios become visible to others in ++ * the managed cache to avoid duplicate additions for unaligned extents. ++ */ + if (folio->private == Z_EROFS_PREALLOCATED_FOLIO) { ++ folio->private = NULL; + tocache = true; + goto out_tocache; + } +@@ -1548,14 +1555,8 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, + } + return; + } +- /* +- * Already linked with another pcluster, which only appears in +- * crafted images by fuzzers for now. But handle this anyway. +- */ +- tocache = false; /* use temporary short-lived pages */ + } else { + DBG_BUGON(1); /* referenced managed folios can't be truncated */ +- tocache = true; + } + folio_unlock(folio); + folio_put(folio); +-- +2.53.0 + diff --git a/queue-6.18/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-6.18/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..31a344de73 --- /dev/null +++ b/queue-6.18/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From 08c3a93c0f0b9a46e23ffdaab46dbd1f7561934b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch b/queue-6.18/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch new file mode 100644 index 0000000000..a658f08a62 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch @@ -0,0 +1,52 @@ +From 2b42909b365d2f78fc50b75b0f4f03166452b4eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:33 +0100 +Subject: firmware: arm_ffa: Align RxTx buffer size before mapping + +From: Sudeep Holla + +[ Upstream commit 0399e3f872ca3d78044bb715a73ea645806d2c7b ] + +Commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during +RXTX_MAP") advertises PAGE_ALIGN(rxtx_bufsz) to firmware when mapping the +buffers but the driver continues to stores the minimum FF-A buffer size +in drv_info->rxtx_bufsz which is used elsewhere in the driver. + +Align the size before storing it so that the allocation, validation and +FFA_RXTX_MAP all use the same buffer size. + +Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") +Cc: Sebastian Ene +Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com +Reviewed-by: Sebastian Ene +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index f96bb84af55e3..70f34f58f3832 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -2102,6 +2102,7 @@ static int __init ffa_init(void) + rxtx_bufsz = SZ_4K; + } + ++ rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); + drv_info->rxtx_bufsz = rxtx_bufsz; + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { +@@ -2117,7 +2118,7 @@ static int __init ffa_init(void) + + ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), + virt_to_phys(drv_info->rx_buffer), +- PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE); ++ rxtx_bufsz / FFA_PAGE_SIZE); + if (ret) { + pr_err("failed to register FFA RxTx buffers\n"); + goto free_pages; +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch b/queue-6.18/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch new file mode 100644 index 0000000000..bc80eb4e67 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch @@ -0,0 +1,100 @@ +From 7da0d3c768709e3d8f028e7f49ea31c291ab681e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:30 +0100 +Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies + +From: Sudeep Holla + +[ Upstream commit 3974ea1938406f9bfa7c1f48d4e43533f447bb08 ] + +The register-based PARTITION_INFO_GET path trusted the firmware-provided +indices when copying partition descriptors into the caller buffer. +Reject inconsistent counts or index progressions so the copy loop cannot +write past the allocated array. + +Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-6-8595ae450034@kernel.org +(fixed cur_idx when exactly one descriptor in the first fragment) +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++------ + 1 file changed, 23 insertions(+), 6 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 287db85286106..e597a9cf64dc9 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -319,6 +319,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + #define PART_INFO_ID_MASK GENMASK(15, 0) + #define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) + #define PART_INFO_PROPS_MASK GENMASK(63, 32) ++#define FFA_PART_INFO_GET_REGS_FIRST_REG 3 ++#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 ++#define FFA_PART_INFO_GET_REGS_MAX_DESC \ ++ (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ ++ FFA_PART_INFO_GET_REGS_FIRST_REG) / \ ++ FFA_PART_INFO_GET_REGS_REGS_PER_DESC) + #define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) + #define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) + #define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) +@@ -326,15 +332,13 @@ static int + __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + struct ffa_partition_info *buffer, int num_parts) + { +- u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; ++ u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0; + struct ffa_partition_info *buf = buffer; + ffa_value_t partition_info; + + do { + __le64 *regs; +- int idx; +- +- start_idx = prev_idx ? prev_idx + 1 : 0; ++ int idx, nr_desc, buf_idx; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_PARTITION_INFO_GET_REGS, +@@ -350,15 +354,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + count = PARTITION_COUNT(partition_info.a2); + if (!buffer || !num_parts) /* count only */ + return count; ++ if (count > num_parts) ++ return -EINVAL; + + cur_idx = CURRENT_INDEX(partition_info.a2); ++ if (cur_idx < start_idx || cur_idx >= count) ++ return -EINVAL; ++ ++ nr_desc = cur_idx - start_idx + 1; ++ if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) ++ return -EINVAL; ++ ++ buf_idx = buf - buffer; ++ if (buf_idx + nr_desc > num_parts) ++ return -EINVAL; ++ + tag = UUID_INFO_TAG(partition_info.a2); + buf_sz = PARTITION_INFO_SZ(partition_info.a2); + if (buf_sz > sizeof(*buffer)) + buf_sz = sizeof(*buffer); + + regs = (void *)&partition_info.a3; +- for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) { ++ for (idx = 0; idx < nr_desc; idx++, buf++) { + union { + uuid_t uuid; + u64 regs[2]; +@@ -376,7 +393,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + uuid_copy(&buf->uuid, &uuid_regs.uuid); + regs += 3; + } +- prev_idx = cur_idx; ++ start_idx = cur_idx + 1; + + } while (cur_idx < (count - 1)); + +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-6.18/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..58a8982d14 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From 4ad27ba56c6ac552a1fe374d82a20a8dc8e642e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index 50bfe56c755e9..6779923e35c55 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch b/queue-6.18/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch new file mode 100644 index 0000000000..9f5d8775e5 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch @@ -0,0 +1,45 @@ +From 34ba8db8ccd0d3f944e5645064bc37dbb2ebde05 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:28 +0100 +Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in + workqueue + +From: Sudeep Holla + +[ Upstream commit 9985d5357ed93af0d1933969c247e966957730e1 ] + +Per-vcpu notification handling already runs from a per-cpu work item on +the target cpu. Routing that path back through smp_call_function_single() +re-enters the call-function IPI path and executes the notification +handler with interrupts disabled. That makes the framework path unsafe, +since it takes a mutex, allocates memory with GFP_KERNEL, and invokes +client callbacks. + +Handle per-vcpu self notifications directly from the existing per-cpu +work item instead. This keeps the per-vcpu path in task context and +avoids the extra IPI hop entirely. + +Fixes: 3a3e2b83e805 ("firmware: arm_ffa: Avoid queuing work when running on the worker queue") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-4-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index cf7f913a55b6e..30f8e0ff694fb 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1538,7 +1538,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work) + struct ffa_drv_info *info = container_of(work, struct ffa_drv_info, + notif_pcpu_work); + +- ffa_self_notif_handle(smp_processor_id(), true, info); ++ notif_get_and_handle(info); + } + + static const struct ffa_info_ops ffa_drv_info_ops = { +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch b/queue-6.18/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch new file mode 100644 index 0000000000..a1d5875c01 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch @@ -0,0 +1,53 @@ +From f17685ad1e730051980fb87d73281eed7c506c45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:35 +0100 +Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup + +From: Sudeep Holla + +[ Upstream commit a6848a50404eefb6f0b131c21881a2d8d21b31a9 ] + +ffa_sched_recv_cb_update() used list_for_each_entry_safe() to search for +a matching partition and then tested the iterator against NULL. That is +not a valid end-of-list check for circular lists and can fall through +with an invalid pointer. Use a normal iterator and detect the not-found +case correctly before touching the partition state. + +Fixes: be61da938576 ("firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI callback") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-11-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index f518c87031022..827aac08a8e9e 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1203,7 +1203,7 @@ static int + ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + void *cb_data, bool is_registration) + { +- struct ffa_dev_part_info *partition = NULL, *tmp; ++ struct ffa_dev_part_info *partition = NULL; + struct list_head *phead; + bool cb_valid; + +@@ -1216,11 +1216,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + return -EINVAL; + } + +- list_for_each_entry_safe(partition, tmp, phead, node) ++ list_for_each_entry(partition, phead, node) + if (partition->dev == dev) + break; + +- if (!partition) { ++ if (&partition->node == phead) { + pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch b/queue-6.18/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch new file mode 100644 index 0000000000..26149d8e2c --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch @@ -0,0 +1,73 @@ +From 08c54466636a197ee96acb2b04c0ef6f3e7b3d18 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:31 +0100 +Subject: firmware: arm_ffa: Keep framework RX release under lock + +From: Sudeep Holla + +[ Upstream commit 2af18f8e36b277730527cacc2256b1332f56aa28 ] + +The framework notification handler drops rx_lock before issuing +FFA_RX_RELEASE, leaving a window where another RX-buffer user can +start a new FF-A transaction before ownership has actually been +returned to firmware. + +Move the FFA_RX_RELEASE calls so they execute while rx_lock is still +held on both the kmemdup() failure path and the normal success path. +While doing that, switch the handler to scoped_guard() to keep the +critical section explicit. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-7-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++---------------- + 1 file changed, 13 insertions(+), 16 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e597a9cf64dc9..c6a9bf3497cf7 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1488,25 +1488,22 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) + return; + +- mutex_lock(&drv_info->rx_lock); ++ scoped_guard(mutex, &drv_info->rx_lock) { ++ msg = drv_info->rx_buffer; ++ buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); ++ if (!buf) { ++ ffa_rx_release(); ++ return; ++ } + +- msg = drv_info->rx_buffer; +- buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); +- if (!buf) { +- mutex_unlock(&drv_info->rx_lock); +- return; ++ target = SENDER_ID(msg->send_recv_id); ++ if (msg->offset >= sizeof(*msg)) ++ uuid_copy(&uuid, &msg->uuid); ++ else ++ uuid_copy(&uuid, &uuid_null); ++ ffa_rx_release(); + } + +- target = SENDER_ID(msg->send_recv_id); +- if (msg->offset >= sizeof(*msg)) +- uuid_copy(&uuid, &msg->uuid); +- else +- uuid_copy(&uuid, &uuid_null); +- +- mutex_unlock(&drv_info->rx_lock); +- +- ffa_rx_release(); +- + read_lock(&drv_info->notify_lock); + cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); + read_unlock(&drv_info->notify_lock); +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-6.18/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..dc84470485 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From 0d8a0662b72be426465fa745c90ea7ff54c6d1e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index d71a2ef335d1c..cf7f913a55b6e 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -2063,7 +2063,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch b/queue-6.18/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch new file mode 100644 index 0000000000..570fca5633 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch @@ -0,0 +1,102 @@ +From e9fc3a80e61e4c26231d23992a453528fc564ab8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:34 +0100 +Subject: firmware: arm_ffa: Snapshot notifier callbacks under lock + +From: Sudeep Holla + +[ Upstream commit 38290b180a4d5746baed796d49f88d56d2f336cd ] + +Both notification handlers currently look up a notifier callback under +notify_lock, drop the lock, and then dereference the returned +notifier entry. A concurrent unregister can delete and free that +entry in the gap, leaving the handler to dereference stale memory. + +Copy the callback pointer and callback data while notify_lock is +still held and invoke the callback only after the lock is dropped. +This keeps the existing callback execution model while removing the +use-after-free window in both the framework and non-framework +notification paths. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-10-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 35 ++++++++++++++++++++----------- + 1 file changed, 23 insertions(+), 12 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 70f34f58f3832..f518c87031022 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1459,20 +1459,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id, + + static void handle_notif_callbacks(u64 bitmap, enum notify_type type) + { ++ ffa_notifier_cb cb; ++ void *cb_data; + int notify_id; +- struct notifier_cb_info *cb_info = NULL; + + for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap; + notify_id++, bitmap >>= 1) { + if (!(bitmap & 1)) + continue; + +- read_lock(&drv_info->notify_lock); +- cb_info = notifier_hnode_get_by_type(notify_id, type); +- read_unlock(&drv_info->notify_lock); ++ scoped_guard(read_lock, &drv_info->notify_lock) { ++ struct notifier_cb_info *cb_info; ++ ++ cb_info = notifier_hnode_get_by_type(notify_id, type); ++ cb = cb_info ? cb_info->cb : NULL; ++ cb_data = cb_info ? cb_info->cb_data : NULL; ++ } + +- if (cb_info && cb_info->cb) +- cb_info->cb(notify_id, cb_info->cb_data); ++ if (cb) ++ cb(notify_id, cb_data); + } + } + +@@ -1480,9 +1485,10 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + { + void *buf; + uuid_t uuid; ++ void *fwk_cb_data; + int notify_id = 0, target; ++ ffa_fwk_notifier_cb fwk_cb; + struct ffa_indirect_msg_hdr *msg; +- struct notifier_cb_info *cb_info = NULL; + size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); + + /* Only one framework notification defined and supported for now */ +@@ -1518,12 +1524,17 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + ffa_rx_release(); + } + +- read_lock(&drv_info->notify_lock); +- cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); +- read_unlock(&drv_info->notify_lock); ++ scoped_guard(read_lock, &drv_info->notify_lock) { ++ struct notifier_cb_info *cb_info; ++ ++ cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, ++ &uuid); ++ fwk_cb = cb_info ? cb_info->fwk_cb : NULL; ++ fwk_cb_data = cb_info ? cb_info->cb_data : NULL; ++ } + +- if (cb_info && cb_info->fwk_cb) +- cb_info->fwk_cb(notify_id, cb_info->cb_data, buf); ++ if (fwk_cb) ++ fwk_cb(notify_id, fwk_cb_data, buf); + kfree(buf); + } + +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch b/queue-6.18/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch new file mode 100644 index 0000000000..10d1ca8728 --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch @@ -0,0 +1,77 @@ +From 4a2ccf93ffac96e52b409c92bc2219bfbab33668 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:29 +0100 +Subject: firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0 + +From: Sudeep Holla + +[ Upstream commit 6d3daa9b8d313f42d52e75590310f26a29b61b44 ] + +For FF-A v1.0 the driver registers a bus notifier to backfill UUID +matching, but the notifier was never unregistered on cleanup paths. +Track the registration state and unregister it during teardown and early +partition-setup failure. + +Fixes: 9dd15934f60d ("firmware: arm_ffa: Move the FF-A v1.0 NULL UUID workaround to bus notifier") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-5-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 30f8e0ff694fb..287db85286106 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -100,6 +100,7 @@ struct ffa_drv_info { + bool mem_ops_native; + bool msg_direct_req2_supp; + bool bitmap_created; ++ bool bus_notifier_registered; + bool notif_enabled; + unsigned int sched_recv_irq; + unsigned int notif_pend_irq; +@@ -1625,6 +1626,15 @@ static struct notifier_block ffa_bus_nb = { + .notifier_call = ffa_bus_notifier, + }; + ++static void ffa_bus_notifier_unregister(void) ++{ ++ if (!drv_info->bus_notifier_registered) ++ return; ++ ++ bus_unregister_notifier(&ffa_bus_type, &ffa_bus_nb); ++ drv_info->bus_notifier_registered = false; ++} ++ + static int ffa_xa_add_partition_info(struct ffa_device *dev) + { + struct ffa_dev_part_info *info; +@@ -1708,6 +1718,8 @@ static void ffa_partitions_cleanup(void) + struct list_head *phead; + unsigned long idx; + ++ ffa_bus_notifier_unregister(); ++ + /* Clean up/free all registered devices */ + ffa_devices_unregister(); + +@@ -1735,11 +1747,14 @@ static int ffa_setup_partitions(void) + ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb); + if (ret) + pr_err("Failed to register FF-A bus notifiers\n"); ++ else ++ drv_info->bus_notifier_registered = true; + } + + count = ffa_partition_probe(&uuid_null, &pbuf); + if (count <= 0) { + pr_info("%s: No partitions found, error %d\n", __func__, count); ++ ffa_bus_notifier_unregister(); + return -EINVAL; + } + +-- +2.53.0 + diff --git a/queue-6.18/firmware-arm_ffa-validate-framework-notification-mes.patch b/queue-6.18/firmware-arm_ffa-validate-framework-notification-mes.patch new file mode 100644 index 0000000000..01d8a655ff --- /dev/null +++ b/queue-6.18/firmware-arm_ffa-validate-framework-notification-mes.patch @@ -0,0 +1,71 @@ +From 85758ca4b71817b16da1c460308448127ed3d1e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:32 +0100 +Subject: firmware: arm_ffa: Validate framework notification message layout + +From: Sudeep Holla + +[ Upstream commit 4a1cc9e96b311d2609a6f963a5e35bd4ae730d97 ] + +Framework notifications carry an indirect message in the shared RX +buffer. Validate the reported offset and size before using them, reject +zero-length payloads, and ensure that any non-header payload starts at +the UUID field rather than in the middle of the message header. + +Use the validated offset and size values for both kmemdup() and the UUID +parsing path so malformed firmware data cannot drive an out-of-bounds +read or an oversized allocation. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-8-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index c6a9bf3497cf7..f96bb84af55e3 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1483,21 +1483,35 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + int notify_id = 0, target; + struct ffa_indirect_msg_hdr *msg; + struct notifier_cb_info *cb_info = NULL; ++ size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); + + /* Only one framework notification defined and supported for now */ + if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) + return; + + scoped_guard(mutex, &drv_info->rx_lock) { ++ u32 offset, size; ++ + msg = drv_info->rx_buffer; +- buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); ++ offset = msg->offset; ++ size = msg->size; ++ ++ if (!size || (offset != min_offset && offset < sizeof(*msg)) || ++ offset > drv_info->rxtx_bufsz || ++ size > drv_info->rxtx_bufsz - offset) { ++ pr_err("invalid framework notification message\n"); ++ ffa_rx_release(); ++ return; ++ } ++ ++ buf = kmemdup((void *)msg + offset, size, GFP_KERNEL); + if (!buf) { + ffa_rx_release(); + return; + } + + target = SENDER_ID(msg->send_recv_id); +- if (msg->offset >= sizeof(*msg)) ++ if (offset >= sizeof(*msg)) + uuid_copy(&uuid, &msg->uuid); + else + uuid_copy(&uuid, &uuid_null); +-- +2.53.0 + diff --git a/queue-6.18/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch b/queue-6.18/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch new file mode 100644 index 0000000000..072d0584bb --- /dev/null +++ b/queue-6.18/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch @@ -0,0 +1,124 @@ +From 2e0ecd7ed67670470206d3ccdd5f9309dc99dda0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 16:46:29 +0900 +Subject: fprobe: Fix unregister_fprobe() to wait for RCU grace period + +From: Masami Hiramatsu (Google) + +[ Upstream commit 657b594b2084b39a4bc6d8493aa2140cb00cea49 ] + +Commit 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") +changed fprobe to register struct fprobe to an rcu-hlist, but it forgot +to wait for RCU GP. Thus there can be use-after-free if the fprobe is +released right after unregistering. This can be happened on fprobe +event and sample module code. + +To fix this issue, add synchronize_rcu() in unregister_fprobe(). + +Note that BPF is OK because fprobe is used as a part of +bpf_kprobe_multi_link. This unregisters its fprobe in +bpf_kprobe_multi_link_release() and it is deallocated via +bpf_kprobe_multi_link_dealloc(), which is invoked from +bpf_link_defer_dealloc_rcu_gp() RCU callback. + +For BPF, this also introduced unregister_fprobe_async() which does +NOT wait for RCU grace priod. + +Link: https://lore.kernel.org/all/177813998919.256460.2809243930741138224.stgit@mhiramat.tok.corp.google.com/ + +Fixes: 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/linux/fprobe.h | 5 +++++ + kernel/trace/bpf_trace.c | 3 ++- + kernel/trace/fprobe.c | 23 +++++++++++++++++++++-- + 3 files changed, 28 insertions(+), 3 deletions(-) + +diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h +index 0a3bcd1718f37..be1b38c981d4d 100644 +--- a/include/linux/fprobe.h ++++ b/include/linux/fprobe.h +@@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter + int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num); + int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); + int unregister_fprobe(struct fprobe *fp); ++int unregister_fprobe_async(struct fprobe *fp); + bool fprobe_is_registered(struct fprobe *fp); + int fprobe_count_ips_from_filter(const char *filter, const char *notfilter); + #else +@@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp) + { + return -EOPNOTSUPP; + } ++static inline int unregister_fprobe_async(struct fprobe *fp) ++{ ++ return -EOPNOTSUPP; ++} + static inline bool fprobe_is_registered(struct fprobe *fp) + { + return false; +diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c +index 70f1292b7ddbd..88f470b31375a 100644 +--- a/kernel/trace/bpf_trace.c ++++ b/kernel/trace/bpf_trace.c +@@ -2371,7 +2371,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) + struct bpf_kprobe_multi_link *kmulti_link; + + kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); +- unregister_fprobe(&kmulti_link->fp); ++ /* Don't wait for RCU GP here. */ ++ unregister_fprobe_async(&kmulti_link->fp); + kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt); + } + +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 6419c8d772731..b9346f4efa6dc 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -1000,14 +1000,15 @@ static int unregister_fprobe_nolock(struct fprobe *fp) + } + + /** +- * unregister_fprobe() - Unregister fprobe. ++ * unregister_fprobe_async() - Unregister fprobe without RCU GP wait + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). ++ * This function will NOT wait until the fprobe is no longer used. + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +-int unregister_fprobe(struct fprobe *fp) ++int unregister_fprobe_async(struct fprobe *fp) + { + guard(mutex)(&fprobe_mutex); + if (!fp || !fprobe_registered(fp)) +@@ -1015,6 +1016,24 @@ int unregister_fprobe(struct fprobe *fp) + + return unregister_fprobe_nolock(fp); + } ++ ++/** ++ * unregister_fprobe() - Unregister fprobe with RCU GP wait ++ * @fp: A fprobe data structure to be unregistered. ++ * ++ * Unregister fprobe (and remove ftrace hooks from the function entries). ++ * This function will block until the fprobe is no longer used. ++ * ++ * Return 0 if @fp is unregistered successfully, -errno if not. ++ */ ++int unregister_fprobe(struct fprobe *fp) ++{ ++ int ret = unregister_fprobe_async(fp); ++ ++ if (!ret) ++ synchronize_rcu(); ++ return ret; ++} + EXPORT_SYMBOL_GPL(unregister_fprobe); + + static int __init fprobe_initcall(void) +-- +2.53.0 + diff --git a/queue-6.18/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch b/queue-6.18/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch new file mode 100644 index 0000000000..4c79926e14 --- /dev/null +++ b/queue-6.18/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch @@ -0,0 +1,55 @@ +From b7f838922230c0c9ef34cb5c59fc20427646639f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 1 May 2026 15:10:58 +0800 +Subject: fs: Fix return in jfs_mkdir and orangefs_mkdir + +From: Hongling Zeng + +[ Upstream commit a7cf1da7ac016490d6a1106f2aa6b602d34e9a12 ] + +Return NULL instead of passing to ERR_PTR while err is zero +Fixes these smatch warnings: + - fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR' + - fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero + to 'ERR_PTR' + +Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") +Signed-off-by: Hongling Zeng +Link: https://patch.msgid.link/20260501071058.1243245-1-zenghongling@kylinos.cn +Reviewed-by: Jori Koolstra +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/jfs/namei.c | 2 +- + fs/orangefs/namei.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c +index 7879c049632b3..553fca69cf425 100644 +--- a/fs/jfs/namei.c ++++ b/fs/jfs/namei.c +@@ -308,7 +308,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, + out1: + + jfs_info("jfs_mkdir: rc:%d", rc); +- return ERR_PTR(rc); ++ return rc ? ERR_PTR(rc) : NULL; + } + + /* +diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c +index bec5475de094d..75e65e72c2d64 100644 +--- a/fs/orangefs/namei.c ++++ b/fs/orangefs/namei.c +@@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + __orangefs_setattr(dir, &iattr); + out: + op_release(new_op); +- return ERR_PTR(ret); ++ return ret ? ERR_PTR(ret) : NULL; + } + + static int orangefs_rename(struct mnt_idmap *idmap, +-- +2.53.0 + diff --git a/queue-6.18/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch b/queue-6.18/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch new file mode 100644 index 0000000000..ec427b0ab9 --- /dev/null +++ b/queue-6.18/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch @@ -0,0 +1,49 @@ +From 41481414df559d4861151fa16a824dd5fe079ce9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 May 2026 20:26:49 +0900 +Subject: fs/statmount: fix slab out-of-bounds write in statmount_mnt_idmap + +From: Junyoung Jang + +[ Upstream commit a3bf0f28d4ba16e1f35f8c983bb04426b87e2a78 ] + +statmount_mnt_idmap() writes one mapping with seq_printf() and then +manually advances seq->count to include the NUL separator. + +If seq_printf() overflows, seq_set_overflow() sets seq->count to +seq->size. The manual seq->count++ changes this to seq->size + 1. +seq_has_overflowed() then no longer detects the overflow. The corrupted +count returns to statmount_string(), which later executes: + + seq->buf[seq->count++] = '\0'; + +This causes a 1-byte NULL out-of-bounds write on the dynamically +allocated seq buffer. + +Fix this by checking for overflow immediately after seq_printf(). + +Fixes: 37c4a9590e1e ("statmount: allow to retrieve idmappings") +Signed-off-by: Junyoung Jang +Link: https://patch.msgid.link/20260504112649.1862936-1-graypanda.inzag@gmail.com +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/mnt_idmapping.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/mnt_idmapping.c b/fs/mnt_idmapping.c +index a37991fdb194d..3640230a4d43b 100644 +--- a/fs/mnt_idmapping.c ++++ b/fs/mnt_idmapping.c +@@ -375,6 +375,8 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_ + continue; + + seq_printf(seq, "%u %u %u", extent->first, lower, extent->count); ++ if (seq_has_overflowed(seq)) ++ return -EAGAIN; + + seq->count++; /* mappings are separated by \0 */ + if (seq_has_overflowed(seq)) +-- +2.53.0 + diff --git a/queue-6.18/gcc-plugins-always-define-const_cast_gimple-and-cons.patch b/queue-6.18/gcc-plugins-always-define-const_cast_gimple-and-cons.patch new file mode 100644 index 0000000000..db6ba24a75 --- /dev/null +++ b/queue-6.18/gcc-plugins-always-define-const_cast_gimple-and-cons.patch @@ -0,0 +1,45 @@ +From 2ceaab0211f56e6a5686671b82d0fc6a18a1322b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 14 Mar 2026 14:24:56 +0100 +Subject: gcc-plugins: Always define CONST_CAST_GIMPLE and CONST_CAST_TREE + +From: Kees Cook + +[ Upstream commit 905c559e51497b8bfdbb68df8be56d2f70f0de8e ] + +For gcc-16, the CONST_CAST macro family was removed. Add back what +we were using in gcc-common.h, as they are simple wrappers. + +See GCC commits: + c3d96ff9e916c02584aa081f03ab999292efbb50 + 458c7926d48959abcb2c1adaa22458e27459a551 + +Suggested-by: Ingo Saitz +Link: https://lore.kernel.org/lkml/ab6OKoay0OWkywjK@spatz.zoo +Fixes: 6b90bd4ba40b ("GCC plugin infrastructure") +Tested-by: Ivan Bulatovic +Tested-by: Christopher Cradock +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + scripts/gcc-plugins/gcc-common.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h +index 8f1b3500f8e2d..abb1964c44d4e 100644 +--- a/scripts/gcc-plugins/gcc-common.h ++++ b/scripts/gcc-plugins/gcc-common.h +@@ -309,7 +309,9 @@ typedef const gimple *const_gimple_ptr; + #define gimple gimple_ptr + #define const_gimple const_gimple_ptr + #undef CONST_CAST_GIMPLE +-#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) ++#define CONST_CAST_GIMPLE(X) const_cast((X)) ++#undef CONST_CAST_TREE ++#define CONST_CAST_TREE(X) const_cast((X)) + + /* gimple related */ + static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +-- +2.53.0 + diff --git a/queue-6.18/gpio-aggregator-fix-a-potential-use-after-free.patch b/queue-6.18/gpio-aggregator-fix-a-potential-use-after-free.patch new file mode 100644 index 0000000000..641b13d044 --- /dev/null +++ b/queue-6.18/gpio-aggregator-fix-a-potential-use-after-free.patch @@ -0,0 +1,41 @@ +From 73044417702b6e0ec1e779a46400d2492e40a4ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 10:49:11 +0200 +Subject: gpio: aggregator: fix a potential use-after-free + +From: Bartosz Golaszewski + +[ Upstream commit 30c073cab97afb31901f94de9605177b6b84367e ] + +On error we free aggr->lookups->dev_id before removing the entry from +the lookup table. If a concurrent thread calls gpiod_find() before we +remove the entry, it could iterate over the list and call +gpiod_match_lookup_table() which unconditionally dereferences dev_id +when calling strcmp(). Reverse the order of cleanup. + +Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260520084911.27938-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index 416f265d09d07..a68665700733d 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -970,8 +970,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + return 0; + + err_remove_lookup_table: +- kfree(aggr->lookups->dev_id); + gpiod_remove_lookup_table(aggr->lookups); ++ kfree(aggr->lookups->dev_id); + err_remove_swnode: + fwnode_remove_software_node(swnode); + err_remove_lookups: +-- +2.53.0 + diff --git a/queue-6.18/gpio-aggregator-lock-device-when-calling-device_is_b.patch b/queue-6.18/gpio-aggregator-lock-device-when-calling-device_is_b.patch new file mode 100644 index 0000000000..7f6f959c3c --- /dev/null +++ b/queue-6.18/gpio-aggregator-lock-device-when-calling-device_is_b.patch @@ -0,0 +1,43 @@ +From 14053a0543b5295084d287ca9c6ac588fcd9ca81 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 11:53:18 +0200 +Subject: gpio: aggregator: lock device when calling device_is_bound() + +From: Bartosz Golaszewski + +[ Upstream commit 598a2b3e2e0e6aa2e9f7843c96c45b5ea11e0411 ] + +The kerneldoc for device_is_bound() says it must be called with the +device lock taken. Add missing synchronization to this driver. + +Fixes: 3a27f40b4570 ("gpio: aggregator: stop using dev-sync-probe") +Link: https://patch.msgid.link/20260518-gpio-dev-lock-v1-2-cc4736f3ff0b@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index 8cbebcb4bb1f9..17db07cf92d05 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -969,9 +969,12 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + } + + wait_for_device_probe(); +- if (!device_is_bound(&pdev->dev)) { +- ret = -ENXIO; +- goto err_unregister_pdev; ++ ++ scoped_guard(device, &pdev->dev) { ++ if (!device_is_bound(&pdev->dev)) { ++ ret = -ENXIO; ++ goto err_unregister_pdev; ++ } + } + + aggr->pdev = pdev; +-- +2.53.0 + diff --git a/queue-6.18/gpio-aggregator-remove-the-software-node-when-deacti.patch b/queue-6.18/gpio-aggregator-remove-the-software-node-when-deacti.patch new file mode 100644 index 0000000000..956ed1c8bc --- /dev/null +++ b/queue-6.18/gpio-aggregator-remove-the-software-node-when-deacti.patch @@ -0,0 +1,47 @@ +From 6d63f6121631a838f01d3f9a7d491e135282fbc2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:16:31 +0200 +Subject: gpio: aggregator: remove the software node when deactivating the + aggregator + +From: Bartosz Golaszewski + +[ Upstream commit 61fef83f239ecace1cce716135762a2d9b7b1fc6 ] + +The dynamic software node we create for the aggregator platform device +when using configfs is leaked when the device is deactivated. Destroy it +as the last step in the tear-down path. + +Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") +Reported-by: Geert Uytterhoeven +Closes: https://lore.kernel.org/all/CAMuHMdVZ=XUvJTGdDAjnkxgtw7Uvnn61iOy3XN_5XNZM2anctw@mail.gmail.com/ +Link: https://patch.msgid.link/20260520121631.33976-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index 23987c38a2468..8cbebcb4bb1f9 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -992,11 +992,15 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) + { ++ struct fwnode_handle *swnode; ++ ++ swnode = dev_fwnode(&aggr->pdev->dev); + platform_device_unregister(aggr->pdev); + aggr->pdev = NULL; + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); ++ fwnode_remove_software_node(swnode); + } + + static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, +-- +2.53.0 + diff --git a/queue-6.18/gpio-aggregator-stop-using-dev-sync-probe.patch b/queue-6.18/gpio-aggregator-stop-using-dev-sync-probe.patch new file mode 100644 index 0000000000..c873b77d1d --- /dev/null +++ b/queue-6.18/gpio-aggregator-stop-using-dev-sync-probe.patch @@ -0,0 +1,161 @@ +From 4709d039b3cf804bd159a2665989e011c370e79b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Mar 2026 11:31:12 +0100 +Subject: gpio: aggregator: stop using dev-sync-probe + +From: Bartosz Golaszewski + +[ Upstream commit 3a27f40b457053e6112a63d14590e4a3ff553b44 ] + +dev-err-probe is an overengineered solution to a simple problem. Use a +combination of wait_for_probe() and device_is_bound() to synchronously +wait for the platform device to probe. + +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260327-gpio-kill-dev-sync-probe-v1-2-efac254f1a1d@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 61fef83f239e ("gpio: aggregator: remove the software node when deactivating the aggregator") +Signed-off-by: Sasha Levin +--- + drivers/gpio/Kconfig | 1 - + drivers/gpio/gpio-aggregator.c | 38 +++++++++++++++++++--------------- + 2 files changed, 21 insertions(+), 18 deletions(-) + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 4d644dcecad93..3b7284938babf 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1968,7 +1968,6 @@ menu "Virtual GPIO drivers" + config GPIO_AGGREGATOR + tristate "GPIO Aggregator" + select CONFIGFS_FS +- select DEV_SYNC_PROBE + help + Say yes here to enable the GPIO Aggregator, which provides a way to + aggregate existing GPIO lines into a new virtual GPIO chip. +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index a68665700733d..23987c38a2468 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -32,8 +32,6 @@ + #include + #include + +-#include "dev-sync-probe.h" +- + #define AGGREGATOR_MAX_GPIOS 512 + #define AGGREGATOR_LEGACY_PREFIX "_sysfs" + +@@ -42,7 +40,7 @@ + */ + + struct gpio_aggregator { +- struct dev_sync_probe_data probe_data; ++ struct platform_device *pdev; + struct config_group group; + struct gpiod_lookup_table *lookups; + struct mutex lock; +@@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) + { + lockdep_assert_held(&aggr->lock); + +- return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); ++ return aggr->pdev && platform_get_drvdata(aggr->pdev); + } + + /* Only aggregators created via legacy sysfs can be "activating". */ +@@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) + { + lockdep_assert_held(&aggr->lock); + +- return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); ++ return aggr->pdev && !platform_get_drvdata(aggr->pdev); + } + + static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) +@@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + { + struct platform_device_info pdevinfo; + struct gpio_aggregator_line *line; ++ struct platform_device *pdev; + struct fwnode_handle *swnode; + unsigned int n = 0; + int ret = 0; +@@ -963,12 +962,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + gpiod_add_lookup_table(aggr->lookups); + +- ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); +- if (ret) ++ pdev = platform_device_register_full(&pdevinfo); ++ if (IS_ERR(pdev)) { ++ ret = PTR_ERR(pdev); + goto err_remove_lookup_table; ++ } + ++ wait_for_device_probe(); ++ if (!device_is_bound(&pdev->dev)) { ++ ret = -ENXIO; ++ goto err_unregister_pdev; ++ } ++ ++ aggr->pdev = pdev; + return 0; + ++err_unregister_pdev: ++ platform_device_unregister(pdev); + err_remove_lookup_table: + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); +@@ -982,7 +992,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) + { +- dev_sync_probe_unregister(&aggr->probe_data); ++ platform_device_unregister(aggr->pdev); ++ aggr->pdev = NULL; + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); +@@ -1146,7 +1157,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) + + guard(mutex)(&aggr->lock); + +- pdev = aggr->probe_data.pdev; ++ pdev = aggr->pdev; + if (pdev) + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + +@@ -1323,7 +1334,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); +- dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; + } +@@ -1473,12 +1483,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, + scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + +- /* +- * Since the device created by sysfs might be toggled via configfs +- * 'live' attribute later, this initialization is needed. +- */ +- dev_sync_probe_init(&aggr->probe_data); +- + /* Expose to configfs */ + res = configfs_register_group(&gpio_aggregator_subsys.su_group, + &aggr->group); +@@ -1497,7 +1501,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, + goto remove_table; + } + +- aggr->probe_data.pdev = pdev; ++ aggr->pdev = pdev; + module_put(THIS_MODULE); + return count; + +-- +2.53.0 + diff --git a/queue-6.18/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-6.18/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..c670ee37d4 --- /dev/null +++ b/queue-6.18/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From 36b77146ae9b4532ad7536cd912965aa1848d466 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 986312c71678f..89abf7a238d87 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1208,6 +1208,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1215,9 +1216,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-6.18/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch b/queue-6.18/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch new file mode 100644 index 0000000000..f65fc162d5 --- /dev/null +++ b/queue-6.18/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch @@ -0,0 +1,47 @@ +From 806f84e2d069db44a1177da3118a0463caec68ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Apr 2026 10:10:02 +0300 +Subject: HID: intel-thc-hid: Intel-quickspi: Fix some error codes + +From: Dan Carpenter + +[ Upstream commit ae4ac077332ea3341a0f4c0973556c6b7ac5b7a1 ] + +If we have a partial read that is supposed to be treated as failure but +in this code we forgot to set the error code. Return -EINVAL. + +Fixes: 9d8d51735a3a ("HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation") +Signed-off-by: Dan Carpenter +Reviewed-by: Even Xu +Reviewed-by: Mark Pearson +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +index 16f780bc879b1..cb19057f1191b 100644 +--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c ++++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +@@ -94,7 +94,7 @@ static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) + dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", + input_len, read_len); +- return ret; ++ return ret ?: -EINVAL; + } + + input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; +@@ -318,7 +318,7 @@ int reset_tic(struct quickspi_device *qsdev) + dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", + read_len, actual_read_len); +- return ret; ++ return ret ?: -EINVAL; + } + + input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); +-- +2.53.0 + diff --git a/queue-6.18/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-6.18/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..48a278c7ca --- /dev/null +++ b/queue-6.18/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 94fa6b4da69ba8bd7a04db506c3d56f7386f1048 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index f6be3ffee0232..04d3ec360c1dc 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -234,7 +234,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-6.18/hid-uclogic-fix-regression-of-input-name-assignment.patch b/queue-6.18/hid-uclogic-fix-regression-of-input-name-assignment.patch new file mode 100644 index 0000000000..f7b0f6ec70 --- /dev/null +++ b/queue-6.18/hid-uclogic-fix-regression-of-input-name-assignment.patch @@ -0,0 +1,44 @@ +From e393f17b8c38d3258f22a51f4456e87f844049ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 10:33:16 +0200 +Subject: HID: uclogic: Fix regression of input name assignment + +From: Takashi Iwai + +[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] + +The previous fix for adding the devm_kasprintf() return check in the +commit bd07f751208b ("HID: uclogic: Add NULL check in +uclogic_input_configured()") changed the condition of hi->input->name +assignment, and it resulted in missing the proper input device name +when no custom suffix is defined. + +Restore the conditional to the original content to address the +regression. + +Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") +Signed-off-by: Takashi Iwai +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-uclogic-core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c +index 34fb03ae8ee2f..c6db3e7c5fd30 100644 +--- a/drivers/hid/hid-uclogic-core.c ++++ b/drivers/hid/hid-uclogic-core.c +@@ -184,7 +184,9 @@ static int uclogic_input_configured(struct hid_device *hdev, + suffix = "System Control"; + break; + } +- } else { ++ } ++ ++ if (suffix) { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) +-- +2.53.0 + diff --git a/queue-6.18/hwmon-lm90-add-lock-protection-to-lm90_alert.patch b/queue-6.18/hwmon-lm90-add-lock-protection-to-lm90_alert.patch new file mode 100644 index 0000000000..c92a50c0ca --- /dev/null +++ b/queue-6.18/hwmon-lm90-add-lock-protection-to-lm90_alert.patch @@ -0,0 +1,58 @@ +From e34d0123a9c8a387e25d1bd67a280b4ccae7b902 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 14:41:00 -0700 +Subject: hwmon: (lm90) Add lock protection to lm90_alert + +From: Guenter Roeck + +[ Upstream commit 873e919e3101063a7a75989510ccfc125a4391cf ] + +Sashiko reports: + +lm90_alert() executes in the smbus alert context and calls +lm90_update_confreg() to disable the hardware alert line, without +acquiring hwmon_lock. + +Concurrently, sysfs write operations (such as lm90_write_convrate) hold +the hwmon_lock, temporarily modify data->config, and then restore it. + +If an alert interrupt occurs concurrently with a sysfs write, the sysfs +path will overwrite the alert handler's modifications to data->config +and the hardware register. + +This unintentionally re-enables the hardware alert line while the alarm is +still active, causing an interrupt storm. + +Add the missing lock to lm90_alert() to solve the problem. + +Fixes: 7a1d220ccb0cc ("hwmon: (lm90) Introduce function to update configuration register") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index 41ee2309fe8a2..a465a8a7ef5af 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -2963,6 +2963,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + */ + struct lm90_data *data = i2c_get_clientdata(client); + ++ hwmon_lock(data->hwmon_dev); + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { +@@ -2972,6 +2973,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); + } ++ hwmon_unlock(data->hwmon_dev); + } else { + dev_dbg(&client->dev, "Everything OK\n"); + } +-- +2.53.0 + diff --git a/queue-6.18/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch b/queue-6.18/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch new file mode 100644 index 0000000000..58928921d5 --- /dev/null +++ b/queue-6.18/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch @@ -0,0 +1,108 @@ +From 6eeac13db192319546f7b13e2933508f928e1879 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 14:31:49 -0700 +Subject: hwmon: (lm90) Stop work before releasing hwmon device + +From: Guenter Roeck + +[ Upstream commit b09a45601094c7f4ec4db8090b825fa61e169d93 ] + +Sashiko reports: + +In lm90_probe(), the devm action to cancel the alert_work and report_work +(lm90_restore_conf) is registered in lm90_init_client() before +devm_hwmon_device_register_with_info() is called. + +Because devm executes cleanup actions in reverse order during module +unbind or probe failure, the hwmon device is unregistered and freed first. + +If lm90_alert_work() or lm90_report_alarms() runs in the window between +the hwmon device being freed and the delayed works being cancelled, +lm90_update_alarms() will dereference the freed data->hwmon_dev here. + +Fix the problem by canceling the workers separately after registering +the hwmon device and before registering the interrupt handler. This ensures +that the workers are canceled after interrupts are disabled and before +the hwmon device is released. Add "shutdown" flag to indicate that device +shutdown is in progress to prevent workers from being re-armed. + +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index c1f528e292f3d..41ee2309fe8a2 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -738,6 +738,7 @@ struct lm90_data { + struct mutex update_lock; + struct delayed_work alert_work; + struct work_struct report_work; ++ bool shutdown; /* true if shutting down */ + bool valid; /* true if register values are valid */ + bool alarms_valid; /* true if status register values are valid */ + unsigned long last_updated; /* in jiffies */ +@@ -1156,6 +1157,9 @@ static void lm90_report_alarms(struct work_struct *work) + + static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + { ++ if (data->shutdown) ++ return 0; ++ + if (force || !data->alarms_valid || + time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { + struct i2c_client *client = data->client; +@@ -2600,15 +2604,23 @@ static void lm90_restore_conf(void *_data) + struct lm90_data *data = _data; + struct i2c_client *client = data->client; + +- cancel_delayed_work_sync(&data->alert_work); +- cancel_work_sync(&data->report_work); +- + /* Restore initial configuration */ + if (data->flags & LM90_HAVE_CONVRATE) + lm90_write_convrate(data, data->convrate_orig); + lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); + } + ++static void lm90_stop_work(void *_data) ++{ ++ struct lm90_data *data = _data; ++ ++ hwmon_lock(data->hwmon_dev); ++ data->shutdown = true; ++ hwmon_unlock(data->hwmon_dev); ++ cancel_delayed_work_sync(&data->alert_work); ++ cancel_work_sync(&data->report_work); ++} ++ + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + { + struct device_node *np = client->dev.of_node; +@@ -2919,6 +2931,10 @@ static int lm90_probe(struct i2c_client *client) + + data->hwmon_dev = hwmon_dev; + ++ err = devm_add_action_or_reset(&client->dev, lm90_stop_work, data); ++ if (err) ++ return err; ++ + if (client->irq) { + dev_dbg(dev, "IRQ: %d\n", client->irq); + err = devm_request_threaded_irq(dev, client->irq, +@@ -2947,7 +2963,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + */ + struct lm90_data *data = i2c_get_clientdata(client); + +- if ((data->flags & LM90_HAVE_BROKEN_ALERT) && ++ if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); +-- +2.53.0 + diff --git a/queue-6.18/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-6.18/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..2cad84bfff --- /dev/null +++ b/queue-6.18/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From cb589338d73bcf379c095ade284e03b0e5987946 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index 9fc8681cc58ea..270f5a00ece3a 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + int ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-6.18/ice-fix-setting-rss-vsi-hash-for-e830.patch b/queue-6.18/ice-fix-setting-rss-vsi-hash-for-e830.patch new file mode 100644 index 0000000000..50ef3a24b1 --- /dev/null +++ b/queue-6.18/ice-fix-setting-rss-vsi-hash-for-e830.patch @@ -0,0 +1,56 @@ +From 33989d1d6333bec3d337c0c1da6bb1cab53d16ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:14 -0700 +Subject: ice: fix setting RSS VSI hash for E830 + +From: Marcin Szycik + +[ Upstream commit b3cda96feb60d91fe88d52b974ff110dcfa91239 ] + +ice_set_rss_hfunc() performs a VSI update, in which it sets hashing +function, leaving other VSI options unchanged. However, ::q_opt_flags is +mistakenly set to the value of another field, instead of its original +value, probably due to a typo. What happens next is hardware-dependent: + +On E810, only the first bit is meaningful (see +ICE_AQ_VSI_Q_OPT_PE_FLTR_EN) and can potentially end up in a different +state than before VSI update. + +On E830, some of the remaining bits are not reserved. Setting them +to some unrelated values can cause the firmware to reject the update +because of invalid settings, or worse - succeed. + +Reproducer: + sudo ethtool -X $PF1 equal 8 + +Output in dmesg: + Failed to configure RSS hash for VSI 6, error -5 + +Fixes: 352e9bf23813 ("ice: enable symmetric-xor RSS for Toeplitz hash function") +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Signed-off-by: Marcin Szycik +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index d42b65e29e485..dc5d821bf3348 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -8075,7 +8075,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) + ctx->info.q_opt_rss |= + FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); + ctx->info.q_opt_tc = vsi->info.q_opt_tc; +- ctx->info.q_opt_flags = vsi->info.q_opt_rss; ++ ctx->info.q_opt_flags = vsi->info.q_opt_flags; + + err = ice_update_vsi(hw, vsi->idx, ctx, NULL); + if (err) { +-- +2.53.0 + diff --git a/queue-6.18/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch b/queue-6.18/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch new file mode 100644 index 0000000000..77ccf96153 --- /dev/null +++ b/queue-6.18/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch @@ -0,0 +1,79 @@ +From bd010d2d05035c9df5a5b15af3bf23a54e7d20b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:11 -0700 +Subject: ice: ptp: serialize E825 PHY timer start with PTP lock + +From: Grzegorz Nitka + +[ Upstream commit 781ff8f2d575a794a2a4f11605288ae06757f5eb ] + +ice_start_phy_timer_eth56g() programs TIMETUS registers and issues +INIT_INCVAL without holding the global PTP semaphore. + +This allows concurrent PTP command paths to interleave with PHY timer +start, which can make the sequence fail and leave timer initialization +inconsistent. + +Take the PTP lock around TIMETUS registers programming and INIT_INCVAL +command execution, and make sure the lock is released on all error paths. + +Keep the subsequent sync step outside of this critical section, since +ice_sync_phy_timer_eth56g() takes the same semaphore internally. + +Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products") +Reviewed-by: Arkadiusz Kubalewski +Signed-off-by: Grzegorz Nitka +Reviewed-by: Aleksandr Loktionov +Tested-by: Alexander Nowlin +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +index 30b7839398165..0f378e68f72f1 100644 +--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c ++++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +@@ -2141,16 +2141,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + } + incval = (u64)hi << 32 | lo; + ++ if (!ice_ptp_lock(hw)) { ++ dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n"); ++ return -EBUSY; ++ } ++ + err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval); + if (err) +- return err; ++ goto err_ptp_unlock; + + err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL); + if (err) +- return err; ++ goto err_ptp_unlock; + + ice_ptp_exec_tmr_cmd(hw); + ++ ice_ptp_unlock(hw); ++ + err = ice_sync_phy_timer_eth56g(hw, port); + if (err) + return err; +@@ -2166,6 +2173,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port); + + return 0; ++ ++err_ptp_unlock: ++ ice_ptp_unlock(hw); ++ return err; + } + + /** +-- +2.53.0 + diff --git a/queue-6.18/ice-ptp-use-primary-nac-semaphore-on-e825.patch b/queue-6.18/ice-ptp-use-primary-nac-semaphore-on-e825.patch new file mode 100644 index 0000000000..175def1092 --- /dev/null +++ b/queue-6.18/ice-ptp-use-primary-nac-semaphore-on-e825.patch @@ -0,0 +1,69 @@ +From 4587eb6d46e721e960a50a7dd076354391ca253c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:12 -0700 +Subject: ice: ptp: use primary NAC semaphore on E825 + +From: Grzegorz Nitka + +[ Upstream commit 7b28523546c7e4adbb8436f2986efcfc8382985e ] + +For E825 2xNAC configurations, PTP semaphore operations must hit the +primary NAC register block so both sides coordinate on the same lock. + +Commit e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 +devices") updated other primary-only PTP register accesses to +use the primary NAC on non-primary functions, but left ice_ptp_lock() +and ice_ptp_unlock() operating on the local NAC. As a result, secondary +NAC PTP paths can take a different semaphore than the primary side. + +Select the primary hardware in ice_ptp_lock() and ice_ptp_unlock() when +the current function is not primary, keeping semaphore operations +symmetric and consistent with the rest of the 2xNAC PTP register access +path. + +Fixes: e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 devices") +Reviewed-by: Arkadiusz Kubalewski +Signed-off-by: Grzegorz Nitka +Reviewed-by: Aleksandr Loktionov +Tested-by: Alexander Nowlin +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-6-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +index 0f378e68f72f1..99bf38cf352a2 100644 +--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c ++++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +@@ -5264,9 +5264,13 @@ static void ice_ptp_init_phy_e830(struct ice_ptp_hw *ptp) + */ + bool ice_ptp_lock(struct ice_hw *hw) + { ++ struct ice_pf *pf = container_of(hw, struct ice_pf, hw); + u32 hw_lock; + int i; + ++ if (!ice_is_primary(hw)) ++ hw = ice_get_primary_hw(pf); ++ + #define MAX_TRIES 15 + + for (i = 0; i < MAX_TRIES; i++) { +@@ -5293,6 +5297,11 @@ bool ice_ptp_lock(struct ice_hw *hw) + */ + void ice_ptp_unlock(struct ice_hw *hw) + { ++ struct ice_pf *pf = container_of(hw, struct ice_pf, hw); ++ ++ if (!ice_is_primary(hw)) ++ hw = ice_get_primary_hw(pf); ++ + wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); + } + +-- +2.53.0 + diff --git a/queue-6.18/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch b/queue-6.18/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch new file mode 100644 index 0000000000..da4e6f234d --- /dev/null +++ b/queue-6.18/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch @@ -0,0 +1,91 @@ +From 6029aaee60034a2315247cb8adedd54682e465f9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:12 -0700 +Subject: idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init() + +From: Emil Tantilov + +[ Upstream commit da4f76b6a84ede14a71282ef841768299ead0221 ] + +In idpf_ptp_init(), read_dev_clk_lock is initialized after +ptp_schedule_worker() had already been called (and after +idpf_ptp_settime64() could reach the lock). The PTP aux worker +fires immediately upon scheduling and can call into +idpf_ptp_read_src_clk_reg_direct(), which takes +spin_lock(&ptp->read_dev_clk_lock) on an uninitialized lock, triggering +the lockdep "non-static key" warning: + +[12973.796587] idpf 0000:83:00.0: Device HW Reset initiated +[12974.094507] INFO: trying to register non-static key. +... +[12974.097208] Call Trace: +[12974.097213] +[12974.097218] dump_stack_lvl+0x93/0xe0 +[12974.097234] register_lock_class+0x4c4/0x4e0 +[12974.097249] ? __lock_acquire+0x427/0x2290 +[12974.097259] __lock_acquire+0x98/0x2290 +[12974.097272] lock_acquire+0xc6/0x310 +[12974.097281] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097311] ? lockdep_hardirqs_on_prepare+0xde/0x190 +[12974.097318] ? finish_task_switch.isra.0+0xd2/0x350 +[12974.097330] ? __pfx_ptp_aux_kworker+0x10/0x10 [ptp] +[12974.097343] _raw_spin_lock+0x30/0x40 +[12974.097353] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097373] idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097391] ? kthread_worker_fn+0x88/0x3d0 +[12974.097404] ? kthread_worker_fn+0x4e/0x3d0 +[12974.097411] idpf_ptp_update_cached_phctime+0x26/0x120 [idpf] +[12974.097428] ? _raw_spin_unlock_irq+0x28/0x50 +[12974.097436] idpf_ptp_do_aux_work+0x15/0x20 [idpf] +[12974.097454] ptp_aux_kworker+0x20/0x40 [ptp] +[12974.097464] kthread_worker_fn+0xd5/0x3d0 +[12974.097474] ? __pfx_kthread_worker_fn+0x10/0x10 +[12974.097482] kthread+0xf4/0x130 +[12974.097489] ? __pfx_kthread+0x10/0x10 +[12974.097498] ret_from_fork+0x32c/0x410 +[12974.097512] ? __pfx_kthread+0x10/0x10 +[12974.097519] ret_from_fork_asm+0x1a/0x30 +[12974.097540] + +Move the call to spin_lock_init() up a bit to make sure read_dev_clk_lock +is not touched before it's been initialized. + +Fixes: 5cb8805d2366 ("idpf: negotiate PTP capabilities and get PTP clock") +Signed-off-by: Emil Tantilov +Reviewed-by: Madhu Chittim +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Simon Horman +Tested-by: Samuel Salin +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-3-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c +index 0a8b50350b860..31c5593550e1a 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c +@@ -949,6 +949,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) + goto free_ptp; + } + ++ spin_lock_init(&adapter->ptp->read_dev_clk_lock); ++ + err = idpf_ptp_create_clock(adapter); + if (err) + goto free_ptp; +@@ -974,8 +976,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) + goto remove_clock; + } + +- spin_lock_init(&adapter->ptp->read_dev_clk_lock); +- + pci_dbg(adapter->pdev, "PTP init successful\n"); + + return 0; +-- +2.53.0 + diff --git a/queue-6.18/igc-set-tx-buffer-type-for-smd-frames.patch b/queue-6.18/igc-set-tx-buffer-type-for-smd-frames.patch new file mode 100644 index 0000000000..2f570bd5de --- /dev/null +++ b/queue-6.18/igc-set-tx-buffer-type-for-smd-frames.patch @@ -0,0 +1,46 @@ +From 15938d812debf6cc730f92936b66fac79f5a8c98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:15 -0700 +Subject: igc: set tx buffer type for SMD frames + +From: Kohei Enju + +[ Upstream commit 5acc641e590e008caaed480ed9ffae47cf7ecbdf ] + +Sashiko pointed out that igc_fpe_init_smd_frame() initializes +igc_tx_buffer fields for an SMD skb, but does not set the buffer type: +https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp + +Since igc_tx_buffer entries are reused, a stale XDP or XSK type can +remain and make TX completion use the wrong cleanup path. + +Set the buffer type to IGC_TX_BUFFER_TYPE_SKB. + +Fixes: 5422570c0010 ("igc: add support for frame preemption verification") +Signed-off-by: Kohei Enju +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Simon Horman +Tested-by: Avigail Dahan +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-9-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/igc/igc_tsn.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c +index 02dd9f0290a34..52de2bcbadbec 100644 +--- a/drivers/net/ethernet/intel/igc/igc_tsn.c ++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c +@@ -34,6 +34,7 @@ static int igc_fpe_init_smd_frame(struct igc_ring *ring, + return -ENOMEM; + } + ++ buffer->type = IGC_TX_BUFFER_TYPE_SKB; + buffer->skb = skb; + buffer->protocol = 0; + buffer->bytecount = skb->len; +-- +2.53.0 + diff --git a/queue-6.18/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch b/queue-6.18/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch new file mode 100644 index 0000000000..e436d3f27c --- /dev/null +++ b/queue-6.18/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch @@ -0,0 +1,89 @@ +From d784f7e924e36625a1d244c04be719afc8650d42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 10:19:09 -0600 +Subject: io_uring/net: punt IORING_OP_BIND async if it needs file create + +From: Jens Axboe + +[ Upstream commit ccd25890f73c082fe2657ed227b497d6ac5fdc40 ] + +For two reasons: + +1) An opcode cannot block inside io_uring_enter() doing submissions, as + it'll stall the submission side pipeline. + +2) Ending up in sb_start_write() -> __sb_start_write() -> + percpu_down_read_freezable() introduces a new lockdep edge, which it + correctly complains about. + +Check if the socket type is AF_UNIX and has a non-empty pathname. If it +does, mark it REQ_F_FORCE_ASYNC to punt the submission to io-wq rather +than attempt to do it inline. + +Fixes: 7481fd93fa0a ("io_uring: Introduce IORING_OP_BIND") +Reviewed-by: Gabriel Krisman Bertazi +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/net.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/io_uring/net.c b/io_uring/net.c +index ad08f693bccb9..7595850c2217a 100644 +--- a/io_uring/net.c ++++ b/io_uring/net.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1837,11 +1838,29 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) + return IOU_COMPLETE; + } + ++/* ++ * Check if bind request would potentially end up with filename_create(), ++ * which in turn end up in mnt_want_write() which will grab the fs ++ * percpu start write sem. This can trigger a lockdep warning. ++ */ ++static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len) ++{ ++ const struct sockaddr_un *sun; ++ ++ if (io->addr.ss_family != AF_UNIX) ++ return 0; ++ if (addr_len <= offsetof(struct sockaddr_un, sun_path)) ++ return 0; ++ sun = (const struct sockaddr_un *) &io->addr; ++ return sun->sun_path[0] != '\0'; ++} ++ + int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + { + struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); + struct sockaddr __user *uaddr; + struct io_async_msghdr *io; ++ int ret; + + if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) + return -EINVAL; +@@ -1852,7 +1871,12 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + io = io_msg_alloc_async(req); + if (unlikely(!io)) + return -ENOMEM; +- return move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ if (unlikely(ret)) ++ return ret; ++ if (io_bind_file_create(io, bind->addr_len)) ++ req->flags |= REQ_F_FORCE_ASYNC; ++ return 0; + } + + int io_bind(struct io_kiocb *req, unsigned int issue_flags) +-- +2.53.0 + diff --git a/queue-6.18/io_uring-nop-pass-all-errors-to-userspace.patch b/queue-6.18/io_uring-nop-pass-all-errors-to-userspace.patch new file mode 100644 index 0000000000..e32fa7128a --- /dev/null +++ b/queue-6.18/io_uring-nop-pass-all-errors-to-userspace.patch @@ -0,0 +1,42 @@ +From 2deda1613f0cbde1acc13d8a0c78ff73eeed4e81 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:00:44 +0200 +Subject: io_uring/nop: pass all errors to userspace + +From: Alexander A. Klimov + +[ Upstream commit e97ff8b62d4690c69297f0f6de874f0564cc01a4 ] + +This fixes an inconsistency where io_nop() called req_set_fail() +based on ret, but passed just nop->result to userspace. +Originally, ret is a even copy of nop->result, but is set to an error +when such happens subsequently. Now that's also passed to userspace. + +Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") +Signed-off-by: Alexander A. Klimov +Link: https://patch.msgid.link/20260520180045.538533-1-grandmaster@al2klimov.de +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/nop.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/io_uring/nop.c b/io_uring/nop.c +index 3caf07878f8ac..f5c9969e7f64a 100644 +--- a/io_uring/nop.c ++++ b/io_uring/nop.c +@@ -79,9 +79,9 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) + if (ret < 0) + req_set_fail(req); + if (nop->flags & IORING_NOP_CQE32) +- io_req_set_res32(req, nop->result, 0, nop->extra1, nop->extra2); ++ io_req_set_res32(req, ret, 0, nop->extra1, nop->extra2); + else +- io_req_set_res(req, nop->result, 0); ++ io_req_set_res(req, ret, 0); + if (nop->flags & IORING_NOP_TW) { + req->io_task_work.func = io_req_task_complete; + io_req_task_work_add(req); +-- +2.53.0 + diff --git a/queue-6.18/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch b/queue-6.18/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch new file mode 100644 index 0000000000..7abcfc96af --- /dev/null +++ b/queue-6.18/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch @@ -0,0 +1,68 @@ +From f5f08571ba8399f8676c32a5ff537434609a0276 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Mar 2026 15:32:29 +0800 +Subject: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jiayuan Chen + +[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] + +On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via +run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. + +After irq_work_single() clears BUSY via atomic_cmpxchg(), it still +dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). + +An irq_work_sync() caller on another CPU that enters after BUSY is cleared +can observe BUSY==0 immediately, return, and free the work before those +accesses complete — causing a use-after-free. + +Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire +irq_work_single() execution is within an RCU read-side critical +section. Then add synchronize_rcu() in irq_work_sync() after +rcuwait_wait_event() to ensure the caller waits for the RCU grace period +before returning, preventing premature frees. + +Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") +Suggested-by: Sebastian Andrzej Siewior +Suggested-by: Steven Rostedt +Signed-off-by: Jiayuan Chen +Signed-off-by: Thomas Gleixner +Reviewed-by: Sebastian Andrzej Siewior +Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev +Signed-off-by: Sasha Levin +--- + kernel/irq_work.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/kernel/irq_work.c b/kernel/irq_work.c +index 73f7e1fd4ab4d..bf411656c3160 100644 +--- a/kernel/irq_work.c ++++ b/kernel/irq_work.c +@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) + !arch_irq_work_has_interrupt()) { + rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), + TASK_UNINTERRUPTIBLE); ++ /* ++ * Ensure irq_work_single() does not access @work ++ * after removing IRQ_WORK_BUSY. It is always ++ * accessed within a RCU-read section. ++ */ ++ synchronize_rcu(); + return; + } + +@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); + + static void run_irq_workd(unsigned int cpu) + { ++ guard(rcu)(); + irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + +-- +2.53.0 + diff --git a/queue-6.18/irqchip-ath79-cpu-remove-unused-function.patch b/queue-6.18/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..731037157c --- /dev/null +++ b/queue-6.18/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From e57933835906642b636d7ce7a3941523367fac5d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-6.18/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch b/queue-6.18/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch new file mode 100644 index 0000000000..2cbb6cfd96 --- /dev/null +++ b/queue-6.18/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch @@ -0,0 +1,51 @@ +From 9dabc67ffe85e540e68173c0dadded520497098e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 23:58:45 +0200 +Subject: kbuild: pacman-pkg: make "rc" releases adhere to pacman versioning + scheme +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Viktor Jägersküpper + +[ Upstream commit 202550713128da20d9381d6d2dc0f6b73839f434 ] + +The package versioning scheme does not enable smooth upgrades from "rc" +releases to the corresponding stable releases (e.g. 7.0.0-rc7 -> 7.0.0) +because pacman considers that a downgrade due to the underscore in +pkgver (e.g. 7.0.0_rc7), see e.g. vercmp(8) for an explanation of the +package version comparison used by pacman. Package versions which are +derived from said releases (e.g. built from git revisions) are +similarly affected. Fix this by modifying pkgver in order to remove the +hyphen from kernel versions containing "-rcN", where N is a +non-negative integer. + +Acked-by: Thomas Weißschuh +Signed-off-by: Viktor Jägersküpper +Reviewed-by: Nathan Chancellor +Tested-by: Nathan Chancellor +Link: https://patch.msgid.link/20260515215913.92481-1-viktor_jaegerskuepper@freenet.de +Fixes: c8578539deba ("kbuild: add script and target to generate pacman package") +Signed-off-by: Nicolas Schier +Signed-off-by: Sasha Levin +--- + scripts/package/PKGBUILD | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD +index 452374d63c244..1213c8e04671e 100644 +--- a/scripts/package/PKGBUILD ++++ b/scripts/package/PKGBUILD +@@ -10,7 +10,7 @@ for pkg in $_extrapackages; do + pkgname+=("${pkgbase}-${pkg}") + done + +-pkgver="${KERNELRELEASE//-/_}" ++pkgver="$(echo "${KERNELRELEASE}" | sed 's/-\(rc[0-9]\+\)/\1/;s/-/_/g')" + # The PKGBUILD is evaluated multiple times. + # Running scripts/build-version from here would introduce inconsistencies. + pkgrel="${KBUILD_REVISION}" +-- +2.53.0 + diff --git a/queue-6.18/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch b/queue-6.18/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch new file mode 100644 index 0000000000..a95202023a --- /dev/null +++ b/queue-6.18/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch @@ -0,0 +1,103 @@ +From 633d459b3749ce9ba8969924e1b660d18effe138 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() + +From: Jianpeng Chang + +[ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ] + +When kprobe_add_area_blacklist() iterates through a section like +.kprobes.text, the start address may not correspond to a named symbol. +On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by +commit baaf553d3bc3 ("arm64: Implement +HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag +-fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry +point for ftrace call_ops. These pre-function NOPs sit at the section base +address, before the first named function symbol. The compiler emits a $x +mapping symbol at offset 0x00 to mark the start of code, but +find_kallsyms_symbol() ignores mapping symbols. + +Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no +pre-function NOPs are inserted, the first function starts at offset +0x00, and the bug does not trigger. + +This only affects modules that have a .kprobes.text section (i.e. those +using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead +(like kretprobe_example.ko) blacklist exact function addresses via the +_kprobe_blacklist section and are not affected. + +For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, +the .kprobes.text section layout is: + + offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) + offset 0x08: handler_post (64 bytes) + offset 0x50: handler_pre (68 bytes) + +kprobe_add_area_blacklist() starts iterating from the section base +address (offset 0x00), which only has the $x mapping symbol. +kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() +for this address, which goes through: + + kallsyms_lookup_size_offset() + -> module_address_lookup() + -> find_kallsyms_symbol() + +find_kallsyms_symbol() scans all module symbols to find the closest +preceding symbol. + +Since no named text symbol exists at offset 0x00, +find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol +whose address is in the temporary image) as the "best" match. The +computed "size" = next_text_symbol - modinfo_symbol spans across +these two unrelated memory regions, creating a blacklist entry with +a bogus range of tens of terabytes. + +Whether this causes a visible failure depends on address randomization, +here is what happens on Raspberry Pi 4/5: + + - On RPi5, the bogus size was ~35 TB. start + size stayed within + 64-bit range, so the blacklist entry covered the entire kernel + text. register_kprobe() in the module's own init function failed + with -EINVAL. + + - On RPi4, the bogus size was ~75 TB. start + size overflowed + 64 bits and wrapped to a small address near zero. The range + check (addr >= start && addr < end) then failed because end + wrapped around, so the bogus entry was accidentally harmless + and kprobes worked by luck. + +The same bug exists on both machines, but randomization determines whether +the integer overflow masks it or not. + +Fix this by adding notrace to the __kprobes macro. Functions in +.kprobes.text are kprobe infrastructure handlers that should never be +traced by ftrace. With notrace, the compiler stops inserting them and the +non-symbol gap at the section start disappears entirely. + +Link: https://lore.kernel.org/all/20260506012706.2785785-1-jianpeng.chang.cn@windriver.com/ + +Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") +Signed-off-by: Jianpeng Chang +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/asm-generic/kprobes.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h +index 060eab094e5a2..5290a2b2e15a0 100644 +--- a/include/asm-generic/kprobes.h ++++ b/include/asm-generic/kprobes.h +@@ -14,7 +14,7 @@ static unsigned long __used \ + _kbl_addr_##fname = (unsigned long)fname; + # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) + /* Use this to forbid a kprobes attach on very low level functions */ +-# define __kprobes __section(".kprobes.text") ++# define __kprobes notrace __section(".kprobes.text") + # define nokprobe_inline __always_inline + #else + # define NOKPROBE_SYMBOL(fname) +-- +2.53.0 + diff --git a/queue-6.18/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch b/queue-6.18/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch new file mode 100644 index 0000000000..8180e3d8cc --- /dev/null +++ b/queue-6.18/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch @@ -0,0 +1,63 @@ +From f4b5f17f6a6982c069d49dcdc2280c666eb7ce67 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 23:27:19 +0900 +Subject: ksmbd: fix durable reconnect error path file lifetime + +From: Junyi Liu + +[ Upstream commit 3515503322f4819277091839eed46b695096aca5 ] + +After a durable reconnect succeeds, ksmbd_reopen_durable_fd() republishes +the same ksmbd_file into the session volatile-id table. If smb2_open() +then takes a later error path, cleanup first calls ksmbd_fd_put(work, fp) +and then unconditionally calls ksmbd_put_durable_fd(dh_info.fp). + +In this case fp and dh_info.fp are the same object. The first put drops the +reconnect lookup reference, but the final durable put can run +__ksmbd_close_fd(NULL, fp). Because the final close is not session-aware, +it can free the file object without removing the volatile-id entry that was +just published into the session table. + +Use the session-aware put for the final reconnect drop when the reconnect +had already succeeded and the error path is cleaning up the republished +file. Earlier reconnect failures, before fp is assigned to dh_info.fp, keep +using the durable-only put path. + +Fixes: 1baff47b81f9 ("ksmbd: fix use-after-free in smb2_open during durable reconnect") +Signed-off-by: Junyi Liu +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 756624b4e90e0..da7b96707186e 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3812,8 +3812,19 @@ int smb2_open(struct ksmbd_work *work) + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + +- if (dh_info.reconnected) +- ksmbd_put_durable_fd(dh_info.fp); ++ if (dh_info.reconnected) { ++ /* ++ * If reconnect succeeded, fp was republished in the ++ * session file table. On a later error, ksmbd_fd_put() ++ * above drops the session reference; drop the durable ++ * lookup reference through the same session-aware path so ++ * final close removes the volatile id before freeing fp. ++ */ ++ if (rc && fp == dh_info.fp) ++ ksmbd_fd_put(work, dh_info.fp); ++ else ++ ksmbd_put_durable_fd(dh_info.fp); ++ } + + kfree(name); + kfree(lc); +-- +2.53.0 + diff --git a/queue-6.18/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-6.18/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..c595094375 --- /dev/null +++ b/queue-6.18/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From b58a2e3d68382af55d44cf6738d114f832faf21f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 7a6af361d2fc6..2b9fd107a69fe 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-6.18/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-6.18/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..958f6b504f --- /dev/null +++ b/queue-6.18/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From 283d9e6b92dd85358445554c6ae89cd5d979ecaa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 2b9fd107a69fe..889380c2702c3 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-6.18/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch b/queue-6.18/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch new file mode 100644 index 0000000000..e82aa58527 --- /dev/null +++ b/queue-6.18/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch @@ -0,0 +1,76 @@ +From 32e8102c50faa7103ad21122b832d455e4113e2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 15:05:07 +0800 +Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions + +From: Tiezhu Yang + +[ Upstream commit 1c856e158fd34ef2c4475a81c1dc386329989938 ] + +KPROBE_HIT_SS and KPROBE_REENTER are two types of fatal recursions that +can not be safely recovered in kprobes. + +KPROBE_HIT_SS means that a kprobe is hit during single-stepping. At +this point, the architecture-specific single-step context is already +active. Nested single-stepping would corrupt the state, as the kprobe +control block (kcb) and hardware registers cannot safely store multiple +levels of stepping state. + +KPROBE_REENTER means that a third-level recursion occurs when a probe +is hit while the system is already handling a nested probe (second- +level). The kcb only provides a single slot (prev_kprobe) to backup the +state. When a third probe is hit, there is no more space to save the +state without corrupting the first-level backup. + +Kprobes work by replacing instructions with breakpoints. In order to +execute the original instruction and continue, it must be moved to a +temporary "single-step" slot. Since there is no backup space left to +set up this slot safely, the CPU would be forced to return to the same +original breakpoint address, triggering an endless loop. + +Currently, the code only prints a warning and returns. This leads to +an infinite re-entry loop as the CPU repeatedly hits the same trap and +a "stuck" CPU core because preemption was disabled at the start of the +handler and never re-enabled in this early return path. + +Fix the logic by: +1. Merging KPROBE_HIT_SS and KPROBE_REENTER cases, as both represent + fatal recursions that cannot be safely recovered. +2. Replacing WARN_ON_ONCE() with BUG() to terminate the system. This + aligns LoongArch with other architectures (x86, arm64, riscv) and + prevents stack overflow while providing diagnostic information. + +Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") +Signed-off-by: Tiezhu Yang +Signed-off-by: Huacai Chen +Signed-off-by: Sasha Levin +--- + arch/loongarch/kernel/kprobes.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c +index 04b5b05715cdc..1985ed30dd16f 100644 +--- a/arch/loongarch/kernel/kprobes.c ++++ b/arch/loongarch/kernel/kprobes.c +@@ -186,16 +186,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) + { + switch (kcb->kprobe_status) { +- case KPROBE_HIT_SS: + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs, kcb, 1); + break; ++ case KPROBE_HIT_SS: + case KPROBE_REENTER: + pr_warn("Failed to recover from reentered kprobes.\n"); + dump_kprobe(p); +- WARN_ON_ONCE(1); ++ BUG(); + break; + default: + WARN_ON(1); +-- +2.53.0 + diff --git a/queue-6.18/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-6.18/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..5ecd782e6e --- /dev/null +++ b/queue-6.18/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From 6e1f12ad42a85a879c734a6611f8149efdbe2fac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index cbc730c7cff29..8e6bd99b22d72 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1856,6 +1856,9 @@ static int ag71xx_probe(struct platform_device *pdev) + ag71xx_int_disable(ag, AG71XX_INT_POLL); + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-6.18/net-airoha-disable-gdm2-forwarding-before-configurin.patch b/queue-6.18/net-airoha-disable-gdm2-forwarding-before-configurin.patch new file mode 100644 index 0000000000..0fcae01e41 --- /dev/null +++ b/queue-6.18/net-airoha-disable-gdm2-forwarding-before-configurin.patch @@ -0,0 +1,54 @@ +From 85b7e40ee022366e02833f83a1fd76ec2548b67f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 15:12:02 +0200 +Subject: net: airoha: Disable GDM2 forwarding before configuring GDM2 loopback + +From: Lorenzo Bianconi + +[ Upstream commit 985d4a55e64e43bd86eeb896b81ceba453301989 ] + +Hw design requires to disable GDM2 forwarding before configuring GDM2 +loopback in airoha_set_gdm2_loopback routine. + +Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") +Tested-by: Madhur Agrawal +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c +index 27d62acfcc39c..9781a6fc9bf9a 100644 +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1800,11 +1800,8 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + u32 val, pse_port, chan; + int src_port; + +- /* Forward the traffic to the proper GDM port */ +- pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 +- : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), +- pse_port); ++ FE_PSE_PORT_DROP); + airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + GDM_STRIP_CRC_MASK); + +@@ -1822,6 +1819,11 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); ++ /* Forward the traffic to the proper GDM port */ ++ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 ++ : FE_PSE_PORT_GDM4; ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ pse_port); + + /* Disable VIP and IFC for GDM2 */ + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); +-- +2.53.0 + diff --git a/queue-6.18/net-airoha-fix-npu-rx-dma-descriptor-bits.patch b/queue-6.18/net-airoha-fix-npu-rx-dma-descriptor-bits.patch new file mode 100644 index 0000000000..b79ec3c260 --- /dev/null +++ b/queue-6.18/net-airoha-fix-npu-rx-dma-descriptor-bits.patch @@ -0,0 +1,52 @@ +From da9c6071b2ca092621c623055a57244eca90db1d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 15:44:57 +0200 +Subject: net: airoha: Fix NPU RX DMA descriptor bits + +From: Christian Marangi + +[ Upstream commit 0cb5a74faa3bdcfa3b18735d554e12c0f615e35d ] + +In an internal review from Airoha, it was notice that the RX DMA descriptor +bits and mask are wrong. These values probably refer to an old NPU firmware +never published. The previous value works correctly but it was reported +that in some specific condition in mixed scenario with both Ethernet and +WiFi offload it's possible that RX DMA descriptor signal wrong value with +the problem to the RX ring or packets getting dropped. + +To handle these specific scenario, apply the new suggested bits mask from +Airoha. + +Correct functionality of both AN7581 NPU and MT7996 variant were verified +and confirmed working. + +Fixes: a7fc8c641cab ("net: airoha: Fix npu rx DMA definitions") +Signed-off-by: Christian Marangi +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260518134530.3683-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/linux/soc/airoha/airoha_offload.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/soc/airoha/airoha_offload.h b/include/linux/soc/airoha/airoha_offload.h +index 0e82f1f4d36c4..d4f6e8124a493 100644 +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -70,9 +70,9 @@ static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, + #define NPU_RX1_DESC_NUM 512 + + /* CTRL */ +-#define NPU_RX_DMA_DESC_LAST_MASK BIT(27) +-#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(26, 14) +-#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(13, 1) ++#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) ++#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) ++#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) + #define NPU_RX_DMA_DESC_DONE_MASK BIT(0) + /* INFO */ + #define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 29) +-- +2.53.0 + diff --git a/queue-6.18/net-bridge-flush-multicast-groups-when-snooping-is-d.patch b/queue-6.18/net-bridge-flush-multicast-groups-when-snooping-is-d.patch new file mode 100644 index 0000000000..4afecf9da9 --- /dev/null +++ b/queue-6.18/net-bridge-flush-multicast-groups-when-snooping-is-d.patch @@ -0,0 +1,67 @@ +From 45a60f2594e1c45bf0149276753cdc09e799dc97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Oct 2025 16:45:37 +0200 +Subject: net: bridge: Flush multicast groups when snooping is disabled + +From: Petr Machata + +[ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] + +When forwarding multicast packets, the bridge takes MDB into account when +IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the +MDB is retained, even though it is not used anymore. + +At the same time, during the time that snooping is disabled, the IGMP / MLD +control packets are obviously ignored, and after the snooping is reenabled, +the administrator has to assume it is out of sync. In particular, missed +join and leave messages would lead to traffic being forwarded to wrong +interfaces. + +Keeping the MDB entries around thus serves no purpose, and just takes +memory. Note also that disabling per-VLAN snooping does actually flush the +relevant MDB entries. + +This patch flushes non-permanent MDB entries as global snooping is +disabled. + +Signed-off-by: Petr Machata +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 5855eb0502085..e9a7e65304017 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4640,6 +4640,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + ++static void br_multicast_del_grps(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_disable_port_ctx(&port->multicast_ctx); ++} ++ + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +@@ -4660,6 +4668,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; ++ br_multicast_del_grps(br); + goto unlock; + } + +-- +2.53.0 + diff --git a/queue-6.18/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-6.18/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..0c6014eae0 --- /dev/null +++ b/queue-6.18/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From 11152070660df30b77b41f230fea9fe8c90054fe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 548b85befbf45..d6ce335f6688b 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -973,12 +973,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2378,6 +2382,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2567,6 +2573,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-6.18/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-6.18/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..32fcd23df4 --- /dev/null +++ b/queue-6.18/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From d1c2fec0318b9039db87eca18f50262cefea9de7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index d6ce335f6688b..4571da0d7a8f6 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1250,37 +1250,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-6.18/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-6.18/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..6c6acdaffd --- /dev/null +++ b/queue-6.18/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 5d9bac3ba442524fac4326166bb1faf9daeecd84 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 63f444e13d6f2..40bec34f06a7b 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -123,6 +123,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1445,6 +1446,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1458,7 +1460,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1498,6 +1499,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1508,6 +1510,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1542,6 +1545,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1550,6 +1554,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1559,6 +1564,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1887,6 +1893,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.18/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-6.18/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..47e2cf2679 --- /dev/null +++ b/queue-6.18/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From a675397878270769154ead58ba39aed921ec74a2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 7cf3ed7215a30..63f444e13d6f2 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1494,6 +1494,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-6.18/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-6.18/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..00ce48ea69 --- /dev/null +++ b/queue-6.18/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From 1b1752fe7b9b30d73e8f4e82a893768ee14970cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 6a2004bbe87f9..7cf3ed7215a30 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1443,10 +1445,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1500,6 +1502,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1550,6 +1553,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1877,6 +1881,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.18/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-6.18/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..e3f400858c --- /dev/null +++ b/queue-6.18/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From 14bd17a74135e488b178914d087471a693b16a28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index fa5857923db4c..b4bfd6c174e78 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-6.18/net-gro-don-t-merge-zcopy-skbs.patch b/queue-6.18/net-gro-don-t-merge-zcopy-skbs.patch new file mode 100644 index 0000000000..39526b54ac --- /dev/null +++ b/queue-6.18/net-gro-don-t-merge-zcopy-skbs.patch @@ -0,0 +1,48 @@ +From 4e02d11c9d31ab4ba37ece0de8558983c4bf801f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 22:44:42 +0200 +Subject: net: gro: don't merge zcopy skbs + +From: Sabrina Dubroca + +[ Upstream commit 4db79a322db8c97f7b73b8a347395ef4d685eb40 ] + +skb_gro_receive() can currently copy frags between the source and GRO +skb, without checking the zerocopy status, and in particular the +SKBFL_MANAGED_FRAG_REFS flag. + +When SKBFL_MANAGED_FRAG_REFS is set, the skb doesn't hold a reference +on the pages in shinfo->frags. Appending those frags to another skb's +frags without fixing up the page refcount can lead to UAF. + +When either the last skb in the GRO chain (the one we would append +frags to) or the source skb is zerocopy, don't merge the skbs. + +Fixes: 753f1ca4e1e5 ("net: introduce managed frags infrastructure") +Reported-by: Huzaifa Sidhpurwala +Signed-off-by: Sabrina Dubroca +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/gro.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/gro.c b/net/core/gro.c +index 867611d171dbb..b5f790a643d49 100644 +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -109,6 +109,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) + if (p->pp_recycle != skb->pp_recycle) + return -ETOOMANYREFS; + ++ if (skb_zcopy(p) || skb_zcopy(skb)) ++ return -ETOOMANYREFS; ++ + if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) || + NAPI_GRO_CB(skb)->flush)) + return -E2BIG; +-- +2.53.0 + diff --git a/queue-6.18/net-lan966x-avoid-unregistering-netdev-on-register-f.patch b/queue-6.18/net-lan966x-avoid-unregistering-netdev-on-register-f.patch new file mode 100644 index 0000000000..421f8b4eee --- /dev/null +++ b/queue-6.18/net-lan966x-avoid-unregistering-netdev-on-register-f.patch @@ -0,0 +1,65 @@ +From 811f65536185429e2a6ce71bcd1f97a24e818950 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 21:43:11 +0900 +Subject: net: lan966x: avoid unregistering netdev on register failure + +From: Myeonghun Pak + +[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] + +lan966x_probe_port() stores the newly allocated net_device in the +port before calling register_netdev(). If register_netdev() fails, +the probe error path calls lan966x_cleanup_ports(), which sees +port->dev and calls unregister_netdev() for a device that was never +registered. + +Destroy the phylink instance created for this port and clear port->dev +before returning the registration error. The common cleanup path now skips +ports without port->dev before reaching the registered netdev cleanup, so +it only handles ports that reached the registered-netdev lifetime. + +This also avoids treating an uninitialized FDMA netdev and the failed port +as a NULL == NULL match in the common cleanup path. + +Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +index 47752d3fde0b1..1179a6e127c52 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +@@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; +- if (!port) ++ if (!port || !port->dev) + continue; + +- if (port->dev) +- unregister_netdev(port->dev); ++ unregister_netdev(port->dev); + + lan966x_xdp_port_deinit(port); + if (lan966x->fdma && lan966x->fdma_ndev == port->dev) +@@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, + err = register_netdev(dev); + if (err) { + dev_err(lan966x->dev, "register_netdev failed\n"); ++ phylink_destroy(phylink); ++ port->phylink = NULL; ++ port->dev = NULL; + return err; + } + +-- +2.53.0 + diff --git a/queue-6.18/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-6.18/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..72d83cbb11 --- /dev/null +++ b/queue-6.18/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 795e277d1bbc898aa93f39657d5b8e37daa94232 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 840c6b8957c90..1986bf493399f 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -77,21 +77,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -251,6 +249,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -269,13 +268,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-6.18/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-6.18/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..cf9fddd9c7 --- /dev/null +++ b/queue-6.18/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From c020dbcff8f5be73937670ffba10a2c295965dd0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 1986bf493399f..5faf4ca75b0f4 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -265,6 +265,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-6.18/net-mlx5-do-not-restore-destination-less-tc-rules.patch b/queue-6.18/net-mlx5-do-not-restore-destination-less-tc-rules.patch new file mode 100644 index 0000000000..c76c148dd6 --- /dev/null +++ b/queue-6.18/net-mlx5-do-not-restore-destination-less-tc-rules.patch @@ -0,0 +1,55 @@ +From 8474a9b55cfc4337c8f9b05f9e42efc3fa62726c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:33:02 +0300 +Subject: net/mlx5: Do not restore destination-less TC rules + +From: Jeroen Massar + +[ Upstream commit 8d0a5af8b1ba598e7340761729801624e7a9330e ] + +After IPsec policy/state TX rules are added, any TC flow rule, which +forwards packets to uplink, is modified to forward to IPsec TX tables. +As these tables are destroyed dynamically, whenever there is no +reference to them, the destinations of this kind of rules must be +restored to uplink, unless there is no destination for that rule. + +The flow rules FLOW_ACTION_ACCEPT, DROP, TRAP, GOTO and SAMPLE do not +have a destination port, and thus out_count = 0. + +At cleanup time of the rules in mlx5_esw_ipsec_modify_flow_dests +we call mlx5_eswitch_restore_ipsec_rule but as the above types +do not have a destination we get an underflow of out_count, as +the port is passed, which is esw_attr->out_count - 1. + +This change avoids calling mlx5_eswitch_restore_ipsec_rule when +there are no output destinations and thus avoids the underflow. + +Fixes: d1569537a837 ("net/mlx5e: Modify and restore TC rules for IPSec TX rules") +Signed-off-by: Jeroen Massar +Reviewed-by: Jianbo Liu +Reviewed-by: Cosmin Ratiu +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +index 3cfe743610d3f..ab50d2c734ede 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +@@ -142,7 +142,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, + + attr = flow->attr; + esw_attr = attr->esw_attr; +- if (esw_attr->out_count - esw_attr->split_count > 1) ++ if (!esw_attr->out_count || ++ esw_attr->out_count - esw_attr->split_count > 1) + return 0; + + err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, +-- +2.53.0 + diff --git a/queue-6.18/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch b/queue-6.18/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch new file mode 100644 index 0000000000..b2d594aa8c --- /dev/null +++ b/queue-6.18/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch @@ -0,0 +1,60 @@ +From cd73eb5ce31f34c20602cd1960e99324861d4370 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:59:00 +0100 +Subject: net/mlx5e: Fix eswitch mode block underflow on IPsec acquire SA + +From: Prathamesh Deshpande + +[ Upstream commit abe003b33223ff33552f291644bf35d9c2f992fb ] + +mlx5e_xfrm_add_state() handles acquire-flow temporary SAs by allocating +software state and skipping hardware offload setup. + +That path jumps to the common success label before taking the eswitch mode +block. After tunnel-mode validation was moved earlier, the common success +label unconditionally calls mlx5_eswitch_unblock_mode(). For acquire SAs, +this decrements esw->offloads.num_block_mode without a matching increment. + +Return directly after installing the acquire SA offload handle, so only the +paths that successfully called mlx5_eswitch_block_mode() call the matching +unblock. + +Fixes: 22239eb258bc ("net/mlx5e: Prevent tunnel reformat when tunnel mode not allowed") +Signed-off-by: Prathamesh Deshpande +Reviewed-by: Simon Horman +Reviewed-by: Tariq Toukan +Link: https://patch.msgid.link/20260510225903.13184-1-prathameshdeshpande7@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 7 ++++--- + 1 file changed, 4 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 f03507a522b4f..51fb857a27665 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +@@ -794,8 +794,10 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, + sa_entry->dev = dev; + sa_entry->ipsec = ipsec; + /* Check if this SA is originated from acquire flow temporary SA */ +- if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) +- goto out; ++ if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) { ++ x->xso.offload_handle = (unsigned long)sa_entry; ++ return 0; ++ } + + err = mlx5e_xfrm_validate_state(priv->mdev, x, extack); + if (err) +@@ -872,7 +874,6 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, + xa_unlock_bh(&ipsec->sadb); + } + +-out: + x->xso.offload_handle = (unsigned long)sa_entry; + if (allow_tunnel_mode) + mlx5_eswitch_unblock_encap(priv->mdev); +-- +2.53.0 + diff --git a/queue-6.18/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch b/queue-6.18/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch new file mode 100644 index 0000000000..3e8841ea7f --- /dev/null +++ b/queue-6.18/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch @@ -0,0 +1,95 @@ +From d6b25ac792afdecf4f7937e99d5bb41e08a68ce9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 09:08:08 +0000 +Subject: net: napi: Avoid gro timer misfiring at end of busypoll + +From: Dragos Tatulea + +[ Upstream commit 58e2330bd45572a6e3d46ea94cf7a9641f43591a ] + +When in irq deferral mode (defer-hard-irqs > 0), a short enough +gro-flush timeout can trigger before NAPI_STATE_SCHED is cleared if the +last poll in busy_poll_stop() takes too long. This can have the effect +of leaving the queue stuck with interrupts disabled and no timer armed +which results in a tx timeout if there is no subsequent busypoll cycle. + +To prevent this, defer the gro-flush timer arm after the last poll. + +Fixes: 7fd3253a7de6 ("net: Introduce preferred busy-polling") +Co-developed-by: Martin Karsten +Signed-off-by: Martin Karsten +Signed-off-by: Dragos Tatulea +Reviewed-by: Tariq Toukan +Reviewed-by: Cosmin Ratiu +Reviewed-by: Joe Damato +Link: https://patch.msgid.link/20260506090808.820559-2-dtatulea@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/dev.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/net/core/dev.c b/net/core/dev.c +index 29f2f35ae5ebe..681d7de89c505 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6777,9 +6777,9 @@ static void skb_defer_free_flush(void) + + #if defined(CONFIG_NET_RX_BUSY_POLL) + +-static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) ++static void __busy_poll_stop(struct napi_struct *napi, unsigned long timeout) + { +- if (!skip_schedule) { ++ if (!timeout) { + gro_normal_list(&napi->gro); + __napi_schedule(napi); + return; +@@ -6789,6 +6789,8 @@ static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) + gro_flush_normal(&napi->gro, HZ >= 1000); + + clear_bit(NAPI_STATE_SCHED, &napi->state); ++ hrtimer_start(&napi->timer, ns_to_ktime(timeout), ++ HRTIMER_MODE_REL_PINNED); + } + + enum { +@@ -6800,8 +6802,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + unsigned flags, u16 budget) + { + struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx; +- bool skip_schedule = false; +- unsigned long timeout; ++ unsigned long timeout = 0; + int rc; + + /* Busy polling means there is a high chance device driver hard irq +@@ -6821,10 +6822,12 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + + if (flags & NAPI_F_PREFER_BUSY_POLL) { + napi->defer_hard_irqs_count = napi_get_defer_hard_irqs(napi); +- timeout = napi_get_gro_flush_timeout(napi); +- if (napi->defer_hard_irqs_count && timeout) { +- hrtimer_start(&napi->timer, ns_to_ktime(timeout), HRTIMER_MODE_REL_PINNED); +- skip_schedule = true; ++ if (napi->defer_hard_irqs_count) { ++ /* A short enough gro flush timeout and long enough ++ * poll can result in timer firing too early. ++ * Timer will be armed later if necessary. ++ */ ++ timeout = napi_get_gro_flush_timeout(napi); + } + } + +@@ -6839,7 +6842,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + trace_napi_poll(napi, rc, budget); + netpoll_poll_unlock(have_poll_lock); + if (rc == budget) +- __busy_poll_stop(napi, skip_schedule); ++ __busy_poll_stop(napi, timeout); + bpf_net_ctx_clear(bpf_net_ctx); + local_bh_enable(); + } +-- +2.53.0 + diff --git a/queue-6.18/net-phy-dp83tc811-add-reading-of-abilities.patch b/queue-6.18/net-phy-dp83tc811-add-reading-of-abilities.patch new file mode 100644 index 0000000000..5125fb88c5 --- /dev/null +++ b/queue-6.18/net-phy-dp83tc811-add-reading-of-abilities.patch @@ -0,0 +1,40 @@ +From bcacd72b8fbd62fd3a41c881f9ecedfbbc0824b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 09:19:47 +0200 +Subject: net: phy: DP83TC811: add reading of abilities + +From: Sven Schuchmann + +[ Upstream commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac ] + +At this time the driver is not listing any speeds +it supports. This should be ETHTOOL_LINK_MODE_100baseT1_Full_BIT +for DP83TC811. Add the missing call for phylib to read the abilities. + +Fixes: b753a9faaf9a ("net: phy: DP83TC811: Introduce support for the DP83TC811 phy") +Suggested-by: Andrew Lunn +Signed-off-by: Sven Schuchmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de +[pabeni@redhat.com: dropped revision history] +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/phy/dp83tc811.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c +index e480c2a074505..252fb12b3e68e 100644 +--- a/drivers/net/phy/dp83tc811.c ++++ b/drivers/net/phy/dp83tc811.c +@@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { + .config_init = dp83811_config_init, + .config_aneg = dp83811_config_aneg, + .soft_reset = dp83811_phy_reset, ++ .get_features = genphy_c45_pma_read_ext_abilities, + .get_wol = dp83811_get_wol, + .set_wol = dp83811_set_wol, + .config_intr = dp83811_config_intr, +-- +2.53.0 + diff --git a/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch b/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch new file mode 100644 index 0000000000..998074cf37 --- /dev/null +++ b/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch @@ -0,0 +1,46 @@ +From eb4c8dcba9f27dc1696f8d7111dbe854b26afc38 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:23:10 +0200 +Subject: net: phy: honor eee_disabled_modes in phy_advertise_eee_all() + +From: Nicolai Buchwitz + +[ Upstream commit 8baa7506d793f0636e3f6f01b01ef7be19674d06 ] + +phy_advertise_eee_all() copies supported_eee into advertising_eee +unconditionally, overwriting any filtering applied during phy_probe() +based on DT eee-broken-* properties or driver-populated +eee_disabled_modes. genphy_c45_ethtool_set_eee() calls this helper +when user space passes an empty advertisement, undoing the filtering. + +Apply the same eee_disabled_modes mask in phy_advertise_eee_all() so +the filtering survives the copy, matching the pattern in phy_probe() +and phy_support_eee(). + +Fixes: b64691274f5d ("net: phy: add helper phy_advertise_eee_all") +Signed-off-by: Nicolai Buchwitz +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-2-05b52626fa68@tipi-net.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phy_device.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 5d76bd1ffe279..78cf05a17f8ff 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2792,7 +2792,8 @@ EXPORT_SYMBOL(phy_advertise_supported); + */ + void phy_advertise_eee_all(struct phy_device *phydev) + { +- linkmode_copy(phydev->advertising_eee, phydev->supported_eee); ++ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, ++ phydev->eee_disabled_modes); + } + EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + +-- +2.53.0 + diff --git a/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch b/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch new file mode 100644 index 0000000000..992a506a50 --- /dev/null +++ b/queue-6.18/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch @@ -0,0 +1,51 @@ +From 8690832ff1d697fcf1f56c7a1c03582022e2fac3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:23:09 +0200 +Subject: net: phy: honor eee_disabled_modes in phy_support_eee() + +From: Nicolai Buchwitz + +[ Upstream commit 3655063e083889ed4b79b7dda9cec65478dce09a ] + +phy_support_eee() copies supported_eee into advertising_eee +unconditionally, overwriting any filtering applied during phy_probe() +based on DT eee-broken-* properties or driver-populated +eee_disabled_modes. MAC drivers that call phy_support_eee() after +probe (e.g. bcmgenet, fec, lan743x, lan78xx, r8169) then cause the PHY +to advertise EEE for modes the user marked as broken. + +The symptom is that ethtool --show-eee on the local interface reports +"not supported" (supported & ~eee_disabled_modes is empty) while the +link partner sees EEE negotiated and active. + +phy_probe() already filters advertising_eee via eee_disabled_modes +after calling of_set_phy_eee_broken(). Apply the same mask in +phy_support_eee() so the filtering survives the copy. + +Fixes: 49168d1980e2 ("net: phy: Add phy_support_eee() indicating MAC support EEE") +Signed-off-by: Nicolai Buchwitz +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-1-05b52626fa68@tipi-net.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phy_device.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index dea8b94286d15..5d76bd1ffe279 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2818,7 +2818,8 @@ EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + */ + void phy_support_eee(struct phy_device *phydev) + { +- linkmode_copy(phydev->advertising_eee, phydev->supported_eee); ++ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, ++ phydev->eee_disabled_modes); + phydev->eee_cfg.tx_lpi_enabled = true; + phydev->eee_cfg.eee_enabled = true; + } +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-annotate-the-data-races.patch b/queue-6.18/net-shaper-annotate-the-data-races.patch new file mode 100644 index 0000000000..a3f5b333e0 --- /dev/null +++ b/queue-6.18/net-shaper-annotate-the-data-races.patch @@ -0,0 +1,116 @@ +From 816296d050dfed3db54f84d30c6afe2a1bdf6bcd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:13:24 -0700 +Subject: net: shaper: annotate the data races + +From: Jakub Kicinski + +[ Upstream commit a3442936dd0523277e20aaf86207c574e755c634 ] + +As previously discussed we don't care about making the shaper +state fully RCU-compliant because the hierarchy itself can't +be dumped in one go over Netlink. Let's annotate the reads +and writes to make that clear. + +The field-by-field assignments will also be useful for the +next commit which adds explicit "valid" field (which we don't +want to override with the current full struct assignment). + +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260515221325.1685455-2-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: b8d7519352ba ("net: shaper: rework the VALID marking (again)") +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 53 ++++++++++++++++++++++++++++++++------------- + 1 file changed, 38 insertions(+), 15 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 23d535f157294..a01de4392eb66 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -138,35 +138,58 @@ static int net_shaper_fill_handle(struct sk_buff *msg, + return -EMSGSIZE; + } + ++static void net_shaper_copy(struct net_shaper *dst, ++ const struct net_shaper *src) ++{ ++ WRITE_ONCE(dst->parent.scope, READ_ONCE(src->parent.scope)); ++ WRITE_ONCE(dst->parent.id, READ_ONCE(src->parent.id)); ++ WRITE_ONCE(dst->handle.scope, READ_ONCE(src->handle.scope)); ++ WRITE_ONCE(dst->handle.id, READ_ONCE(src->handle.id)); ++ ++ WRITE_ONCE(dst->metric, READ_ONCE(src->metric)); ++ WRITE_ONCE(dst->bw_min, READ_ONCE(src->bw_min)); ++ WRITE_ONCE(dst->bw_max, READ_ONCE(src->bw_max)); ++ WRITE_ONCE(dst->burst, READ_ONCE(src->burst)); ++ WRITE_ONCE(dst->priority, READ_ONCE(src->priority)); ++ WRITE_ONCE(dst->weight, READ_ONCE(src->weight)); ++ ++ /* private fields are only used on the write path under the lock */ ++ data_race(dst->leaves = src->leaves); ++} ++ + static int + net_shaper_fill_one(struct sk_buff *msg, + const struct net_shaper_binding *binding, + const struct net_shaper *shaper, + const struct genl_info *info) + { ++ struct net_shaper cur; + void *hdr; + + hdr = genlmsg_iput(msg, info); + if (!hdr) + return -EMSGSIZE; + ++ /* Make a copy to avoid data races */ ++ net_shaper_copy(&cur, shaper); ++ + if (net_shaper_fill_binding(msg, binding, NET_SHAPER_A_IFINDEX) || +- net_shaper_fill_handle(msg, &shaper->parent, ++ net_shaper_fill_handle(msg, &cur.parent, + NET_SHAPER_A_PARENT) || +- net_shaper_fill_handle(msg, &shaper->handle, ++ net_shaper_fill_handle(msg, &cur.handle, + NET_SHAPER_A_HANDLE) || +- ((shaper->bw_min || shaper->bw_max || shaper->burst) && +- nla_put_u32(msg, NET_SHAPER_A_METRIC, shaper->metric)) || +- (shaper->bw_min && +- nla_put_uint(msg, NET_SHAPER_A_BW_MIN, shaper->bw_min)) || +- (shaper->bw_max && +- nla_put_uint(msg, NET_SHAPER_A_BW_MAX, shaper->bw_max)) || +- (shaper->burst && +- nla_put_uint(msg, NET_SHAPER_A_BURST, shaper->burst)) || +- (shaper->priority && +- nla_put_u32(msg, NET_SHAPER_A_PRIORITY, shaper->priority)) || +- (shaper->weight && +- nla_put_u32(msg, NET_SHAPER_A_WEIGHT, shaper->weight))) ++ ((cur.bw_min || cur.bw_max || cur.burst) && ++ nla_put_u32(msg, NET_SHAPER_A_METRIC, cur.metric)) || ++ (cur.bw_min && ++ nla_put_uint(msg, NET_SHAPER_A_BW_MIN, cur.bw_min)) || ++ (cur.bw_max && ++ nla_put_uint(msg, NET_SHAPER_A_BW_MAX, cur.bw_max)) || ++ (cur.burst && ++ nla_put_uint(msg, NET_SHAPER_A_BURST, cur.burst)) || ++ (cur.priority && ++ nla_put_u32(msg, NET_SHAPER_A_PRIORITY, cur.priority)) || ++ (cur.weight && ++ nla_put_u32(msg, NET_SHAPER_A_WEIGHT, cur.weight))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); +@@ -424,7 +447,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- *cur = shapers[i]; ++ net_shaper_copy(cur, &shapers[i]); + smp_wmb(); + __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + } +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch b/queue-6.18/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch new file mode 100644 index 0000000000..d5b05185c9 --- /dev/null +++ b/queue-6.18/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch @@ -0,0 +1,46 @@ +From 9897e9d2b14e0724c1fea5569ebe123f540aab7c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:03 -0700 +Subject: net: shaper: enforce singleton NETDEV scope with id 0 + +From: Jakub Kicinski + +[ Upstream commit b62b29e6de6711f5918940aa6ff2bbab6d6af502 ] + +The NETDEV scope represents a singleton root shaper in the per-device +hierarchy. All code assumes NETDEV shapers have id 0: +net_shaper_default_parent() hardcodes parent->id = 0 when returning +the NETDEV parent for QUEUE/NODE children, and the UAPI documentation +describes NETDEV scope as "the main shaper" (singular, not plural). + +Make sure we reject non-0 IDs. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index b2d85963243fa..d65008b819dc9 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -482,6 +482,12 @@ static int net_shaper_parse_handle(const struct nlattr *attr, + else if (handle->scope == NET_SHAPER_SCOPE_NODE) + id = NET_SHAPER_ID_UNSPEC; + ++ if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { ++ NL_SET_ERR_MSG_ATTR(info->extack, id_attr, ++ "Netdev scope is a singleton, must use ID 0"); ++ return -EINVAL; ++ } ++ + handle->id = id; + return 0; + } +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch b/queue-6.18/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch new file mode 100644 index 0000000000..9ea878e162 --- /dev/null +++ b/queue-6.18/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch @@ -0,0 +1,60 @@ +From df95cf56e4e9b234ae48d1c6432d683208ca5daa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:56 -0700 +Subject: net: shaper: fix trivial ordering issue in net_shaper_commit() + +From: Jakub Kicinski + +[ Upstream commit 235fb5376139c3419f2218349f1fa2f06f24f7ad ] + +We should update the entry before we mark it as valid. + +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 64c9d6cf6756c..92ca3b4c7925d 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -295,6 +295,10 @@ net_shaper_lookup(struct net_shaper_binding *binding, + NET_SHAPER_VALID)) + return NULL; + ++ /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is ++ * valid, its contents must be visible too. ++ */ ++ smp_rmb(); + return xa_load(&hierarchy->shapers, index); + } + +@@ -412,8 +416,9 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + *cur = shapers[i]; ++ smp_wmb(); ++ __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + } + xa_unlock(&hierarchy->shapers); + } +@@ -837,6 +842,10 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, + U32_MAX, NET_SHAPER_VALID)); + ctx->start_index++) { ++ /* Pairs with smp_wmb() in net_shaper_commit(): the entry ++ * is marked VALID, so its contents must be visible too. ++ */ ++ smp_rmb(); + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch b/queue-6.18/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch new file mode 100644 index 0000000000..a02614f424 --- /dev/null +++ b/queue-6.18/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch @@ -0,0 +1,72 @@ +From 5a5f918374135735091d3d31ed2e2753c01134f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:00 -0700 +Subject: net: shaper: fix undersized reply skb allocation in GROUP command + +From: Jakub Kicinski + +[ Upstream commit 0f9a857e34d0f8c018a3e4435c6f0e92e8d2f38c ] + +net_shaper_group_send_reply() writes both the NET_SHAPER_A_IFINDEX +attribute (via net_shaper_fill_binding()) and the nested +NET_SHAPER_A_HANDLE attribute (via net_shaper_fill_handle()), but +the reply skb at the call site in net_shaper_nl_group_doit() is +allocated using net_shaper_handle_size(), which only accounts for +the nested handle. + +The allocation is therefore short by nla_total_size(sizeof(u32)) +(8 bytes) for the IFINDEX attribute. In practice the slab allocator +rounds up the small allocation so the bug is latent, but the size +accounting is wrong and could bite if the reply grew further. + +Introduce net_shaper_group_reply_size() that accounts for the full +reply payload and use it both at the genlmsg_new() call site and in +the defensive WARN_ONCE message. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index ee0d1f0613430..5338842122a2a 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -90,6 +90,12 @@ static int net_shaper_handle_size(void) + nla_total_size(sizeof(u32))); + } + ++static int net_shaper_group_reply_size(void) ++{ ++ return nla_total_size(sizeof(u32)) + /* NET_SHAPER_A_IFINDEX */ ++ net_shaper_handle_size(); /* NET_SHAPER_A_HANDLE */ ++} ++ + static int net_shaper_fill_binding(struct sk_buff *msg, + const struct net_shaper_binding *binding, + u32 type) +@@ -1228,7 +1234,7 @@ static int net_shaper_group_send_reply(struct net_shaper_binding *binding, + free_msg: + /* Should never happen as msg is pre-allocated with enough space. */ + WARN_ONCE(true, "calculated message payload length (%d)", +- net_shaper_handle_size()); ++ net_shaper_group_reply_size()); + nlmsg_free(msg); + return -EMSGSIZE; + } +@@ -1276,7 +1282,7 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + /* Prepare the msg reply in advance, to avoid device operation + * rollback on allocation failure. + */ +- msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); ++ msg = genlmsg_new(net_shaper_group_reply_size(), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto free_leaves; +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-flip-the-polarity-of-the-valid-flag.patch b/queue-6.18/net-shaper-flip-the-polarity-of-the-valid-flag.patch new file mode 100644 index 0000000000..411a6bf006 --- /dev/null +++ b/queue-6.18/net-shaper-flip-the-polarity-of-the-valid-flag.patch @@ -0,0 +1,112 @@ +From 158dad15075bd0f59769497bcefab580521872f4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:55 -0700 +Subject: net: shaper: flip the polarity of the valid flag + +From: Jakub Kicinski + +[ Upstream commit 7cee43fcb0c3f71441d2faaa8c2202b6a88b6bef ] + +The usual way of inserting entries which are not yet fully ready +into XArray is to have a VALID flag. The shaper code has a NOT_VALID +flag. Since XArray code does not let us create entries with marks +already set - the creation of entries is currently not atomic. + +Flip the polarity of the VALID flag. This closes the tiny race +in net_shaper_pre_insert() of entries being created without +the NOT_VALID flag. + +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 34 +++++++++++++++++----------------- + 1 file changed, 17 insertions(+), 17 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index e41a82241230d..64c9d6cf6756c 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -275,11 +275,13 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, + parent->id = 0; + } + +-/* +- * MARK_0 is already in use due to XA_FLAGS_ALLOC, can't reuse such flag as +- * it's cleared by xa_store(). ++/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on ++ * an entry only after the device-side configuration has completed ++ * successfully (see net_shaper_commit()). Lookups and dumps must filter on ++ * this mark to avoid exposing tentative entries inserted by ++ * net_shaper_pre_insert() while the driver call is still in flight. + */ +-#define NET_SHAPER_NOT_VALID XA_MARK_1 ++#define NET_SHAPER_VALID XA_MARK_1 + + static struct net_shaper * + net_shaper_lookup(struct net_shaper_binding *binding, +@@ -289,8 +291,8 @@ net_shaper_lookup(struct net_shaper_binding *binding, + struct net_shaper_hierarchy *hierarchy; + + hierarchy = net_shaper_hierarchy_rcu(binding); +- if (!hierarchy || xa_get_mark(&hierarchy->shapers, index, +- NET_SHAPER_NOT_VALID)) ++ if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, ++ NET_SHAPER_VALID)) + return NULL; + + return xa_load(&hierarchy->shapers, index); +@@ -370,13 +372,10 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, + goto free_id; + } + +- /* Mark 'tentative' shaper inside the hierarchy container. +- * xa_set_mark is a no-op if the previous store fails. ++ /* Insert as 'tentative' (no VALID mark). The mark will be set by ++ * net_shaper_commit() once the driver-side configuration succeeds. + */ +- xa_lock(&hierarchy->shapers); +- prev = __xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_NOT_VALID); +- xa_unlock(&hierarchy->shapers); ++ prev = xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); + if (xa_err(prev)) { + NL_SET_ERR_MSG(extack, "Can't insert shaper into device store"); + kfree_rcu(cur, rcu); +@@ -413,8 +412,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- __xa_clear_mark(&hierarchy->shapers, index, +- NET_SHAPER_NOT_VALID); ++ __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + *cur = shapers[i]; + } + xa_unlock(&hierarchy->shapers); +@@ -431,8 +429,9 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) + return; + + xa_lock(&hierarchy->shapers); +- xa_for_each_marked(&hierarchy->shapers, index, cur, +- NET_SHAPER_NOT_VALID) { ++ xa_for_each(&hierarchy->shapers, index, cur) { ++ if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) ++ continue; + __xa_erase(&hierarchy->shapers, index); + kfree(cur); + } +@@ -836,7 +835,8 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + goto out_unlock; + + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, +- U32_MAX, XA_PRESENT)); ctx->start_index++) { ++ U32_MAX, NET_SHAPER_VALID)); ++ ctx->start_index++) { + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-reject-duplicate-leaves-in-group-request.patch b/queue-6.18/net-shaper-reject-duplicate-leaves-in-group-request.patch new file mode 100644 index 0000000000..010db6f85e --- /dev/null +++ b/queue-6.18/net-shaper-reject-duplicate-leaves-in-group-request.patch @@ -0,0 +1,117 @@ +From 17d4efbbdcd18719bc16c6d9f7c9e217c4bb68d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:57 -0700 +Subject: net: shaper: reject duplicate leaves in GROUP request + +From: Jakub Kicinski + +[ Upstream commit a9a2fa1da619f276580b0d4c5d12efac89e8642b ] + +net_shaper_nl_group_doit() does not deduplicate NET_SHAPER_A_LEAVES +entries. When userspace supplies the same leaf handle twice, the same +old-parent pointer lands twice in old_nodes[]. The cleanup loop double +frees the parent. Of course the same parent may still be in old_nodes[] +twice if we are moving multiple of its leaves. + +Note that this patch also implicitly fixes the fact that the +i >= leaves_count path forgets to set ret. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-4-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 60 +++++++++++++++++++++++++++++++++------------ + 1 file changed, 45 insertions(+), 15 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 92ca3b4c7925d..ae19af13c2247 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -941,6 +941,46 @@ static int net_shaper_handle_cmp(const struct net_shaper_handle *a, + return memcmp(a, b, sizeof(*a)); + } + ++static int net_shaper_parse_leaves(struct net_shaper_binding *binding, ++ struct genl_info *info, ++ const struct net_shaper *node, ++ struct net_shaper *leaves, ++ int leaves_count) ++{ ++ struct nlattr *attr; ++ int i, j, ret, rem; ++ ++ i = 0; ++ nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, ++ genlmsg_data(info->genlhdr), ++ genlmsg_len(info->genlhdr), rem) { ++ if (WARN_ON_ONCE(i >= leaves_count)) ++ return -EINVAL; ++ ++ ret = net_shaper_parse_leaf(binding, attr, info, ++ node, &leaves[i]); ++ if (ret) ++ return ret; ++ ++ /* Reject duplicates */ ++ for (j = 0; j < i; j++) { ++ if (net_shaper_handle_cmp(&leaves[i].handle, ++ &leaves[j].handle)) ++ continue; ++ ++ NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, ++ "Duplicate leaf shaper %d:%d", ++ leaves[i].handle.scope, ++ leaves[i].handle.id); ++ return -EINVAL; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ + static int net_shaper_parent_from_leaves(int leaves_count, + const struct net_shaper *leaves, + struct net_shaper *node, +@@ -1198,10 +1238,9 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + struct net_shaper **old_nodes, *leaves, node = {}; + struct net_shaper_hierarchy *hierarchy; + struct net_shaper_binding *binding; +- int i, ret, rem, leaves_count; ++ int i, ret, leaves_count; + int old_nodes_count = 0; + struct sk_buff *msg; +- struct nlattr *attr; + + if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_LEAVES)) + return -EINVAL; +@@ -1229,19 +1268,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + if (ret) + goto free_leaves; + +- i = 0; +- nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, +- genlmsg_data(info->genlhdr), +- genlmsg_len(info->genlhdr), rem) { +- if (WARN_ON_ONCE(i >= leaves_count)) +- goto free_leaves; +- +- ret = net_shaper_parse_leaf(binding, attr, info, +- &node, &leaves[i]); +- if (ret) +- goto free_leaves; +- i++; +- } ++ ret = net_shaper_parse_leaves(binding, info, &node, ++ leaves, leaves_count); ++ if (ret) ++ goto free_leaves; + + /* Prepare the msg reply in advance, to avoid device operation + * rollback on allocation failure. +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch b/queue-6.18/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch new file mode 100644 index 0000000000..067d8cdd76 --- /dev/null +++ b/queue-6.18/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch @@ -0,0 +1,121 @@ +From 13ff86e23d003ac51ba826c1d8744f56699c6529 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:02 -0700 +Subject: net: shaper: reject handle IDs exceeding internal bit-width + +From: Jakub Kicinski + +[ Upstream commit 8d5806c600fddb907ebe378f9c366d4b52ac3a39 ] + +net_shaper_parse_handle() reads the user-supplied handle ID via +nla_get_u32(), accepting the full u32 range. However, the xarray key +is built by net_shaper_handle_to_index() using +FIELD_PREP(NET_SHAPER_ID_MASK, handle->id), where NET_SHAPER_ID_MASK +is GENMASK(25, 0) - only 26 bits wide. FIELD_PREP silently masks off +the upper bits at runtime. A user-supplied NODE id like 0x04000123 +becomes id 0x123. + +Additionally, a user-supplied id equal to NET_SHAPER_ID_UNSPEC +(0x03FFFFFF, which is NET_SHAPER_ID_MASK itself) would collide with +the sentinel used internally by the group operation to signal +"allocate a new NODE id". + +Reject user-supplied IDs >= NET_SHAPER_ID_MASK (i.e., >= 0x03FFFFFF) +in the policy. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-9-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + Documentation/netlink/specs/net_shaper.yaml | 7 +++++++ + net/shaper/shaper.c | 4 +++- + net/shaper/shaper_nl_gen.c | 7 ++++++- + net/shaper/shaper_nl_gen.h | 2 ++ + 4 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml +index 3f2ad772b64b1..de01f922040a5 100644 +--- a/Documentation/netlink/specs/net_shaper.yaml ++++ b/Documentation/netlink/specs/net_shaper.yaml +@@ -33,6 +33,11 @@ doc: | + @cap-get operation. + + definitions: ++ - ++ type: const ++ name: max-handle-id ++ value: 0x3fffffe ++ scope: kernel + - + type: enum + name: scope +@@ -140,6 +145,8 @@ attribute-sets: + - + name: id + type: u32 ++ checks: ++ max: max-handle-id + doc: | + Numeric identifier of a shaper. The id semantic depends on + the scope. For @queue scope it's the queue id and for @node +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 5338842122a2a..b2d85963243fa 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -21,6 +21,8 @@ + + #define NET_SHAPER_ID_UNSPEC NET_SHAPER_ID_MASK + ++static_assert(NET_SHAPER_ID_UNSPEC == NET_SHAPER_MAX_HANDLE_ID + 1); ++ + struct net_shaper_hierarchy { + struct xarray shapers; + }; +@@ -360,7 +362,7 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, + handle->id == NET_SHAPER_ID_UNSPEC) { + u32 min, max; + +- handle->id = NET_SHAPER_ID_MASK - 1; ++ handle->id = NET_SHAPER_MAX_HANDLE_ID; + max = net_shaper_handle_to_index(handle); + handle->id = 0; + min = net_shaper_handle_to_index(handle); +diff --git a/net/shaper/shaper_nl_gen.c b/net/shaper/shaper_nl_gen.c +index c52abf13ff0c9..16ab88f5eb7b4 100644 +--- a/net/shaper/shaper_nl_gen.c ++++ b/net/shaper/shaper_nl_gen.c +@@ -10,10 +10,15 @@ + + #include + ++/* Integer value ranges */ ++static const struct netlink_range_validation net_shaper_a_handle_id_range = { ++ .max = NET_SHAPER_MAX_HANDLE_ID, ++}; ++ + /* Common nested types */ + const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1] = { + [NET_SHAPER_A_HANDLE_SCOPE] = NLA_POLICY_MAX(NLA_U32, 3), +- [NET_SHAPER_A_HANDLE_ID] = { .type = NLA_U32, }, ++ [NET_SHAPER_A_HANDLE_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &net_shaper_a_handle_id_range), + }; + + const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1] = { +diff --git a/net/shaper/shaper_nl_gen.h b/net/shaper/shaper_nl_gen.h +index 1e20eebdedd71..3e5e7342ffbbc 100644 +--- a/net/shaper/shaper_nl_gen.h ++++ b/net/shaper/shaper_nl_gen.h +@@ -11,6 +11,8 @@ + + #include + ++#define NET_SHAPER_MAX_HANDLE_ID 67108862 ++ + /* Common nested types */ + extern const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1]; + extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1]; +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-reject-queue-scope-handle-with-missing-id.patch b/queue-6.18/net-shaper-reject-queue-scope-handle-with-missing-id.patch new file mode 100644 index 0000000000..da7ab108a1 --- /dev/null +++ b/queue-6.18/net-shaper-reject-queue-scope-handle-with-missing-id.patch @@ -0,0 +1,55 @@ +From 30f8ca9610ee3066536723aa024f5f740be557cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:04 -0700 +Subject: net: shaper: reject QUEUE scope handle with missing id + +From: Jakub Kicinski + +[ Upstream commit ce372e869f9f492f3d5aa9a0ae75ed52c61d2d6f ] + +net_shaper_parse_handle() does not enforce that the user provides +the handle ID. For NODE the ID defaults to UNSPEC for all other +cases it defaults to 0. + +For NETDEV 0 is the only option. For QUEUE defaulting to 0 makes +less intuitive sense. Specifically because the behavior should +(IMHO) be the same for all cases where there may be more than +one ID (QUEUE and NODE). + +We should either document this as intentional or reject. +I picked the latter with no strong conviction. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index d65008b819dc9..23d535f157294 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -477,10 +477,15 @@ static int net_shaper_parse_handle(const struct nlattr *attr, + * shaper (any other value). + */ + id_attr = tb[NET_SHAPER_A_HANDLE_ID]; +- if (id_attr) ++ if (id_attr) { + id = nla_get_u32(id_attr); +- else if (handle->scope == NET_SHAPER_SCOPE_NODE) ++ } else if (handle->scope == NET_SHAPER_SCOPE_NODE) { + id = NET_SHAPER_ID_UNSPEC; ++ } else if (handle->scope == NET_SHAPER_SCOPE_QUEUE) { ++ NL_SET_ERR_ATTR_MISS(info->extack, attr, ++ NET_SHAPER_A_HANDLE_ID); ++ return -EINVAL; ++ } + + if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { + NL_SET_ERR_MSG_ATTR(info->extack, id_attr, +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-reject-reparenting-of-existing-nodes.patch b/queue-6.18/net-shaper-reject-reparenting-of-existing-nodes.patch new file mode 100644 index 0000000000..b3f42db8c1 --- /dev/null +++ b/queue-6.18/net-shaper-reject-reparenting-of-existing-nodes.patch @@ -0,0 +1,90 @@ +From e7869b15255a847e0aa5facbb5b33cf0021ed326 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 16:37:45 -0700 +Subject: net: shaper: Reject reparenting of existing nodes + +From: Mohsin Bashir + +[ Upstream commit a77d5a069d959dc45f5f472d48cba37d8cba0f1c ] + +When an existing node-scope shaper is moved to a different parent +via the group operation, the framework fails to update the leaves +count on both the old and new parent shapers. Only newly created +nodes (handle.id == NET_SHAPER_ID_UNSPEC) trigger the parent +leaves increment at line 1039. + +This causes the parent's leaves counter to diverge from the +actual number of children in the xarray. When the node is later +deleted, pre_del_node() allocates an array sized by the stale +leaves count, but the xarray iteration finds more children than +expected, hitting the WARN_ON_ONCE guard and returning -EINVAL. + +Rather than adding reparenting support with complex leaves count +bookkeeping, reject group calls that attempt to change an existing +node's parent. Updates to an existing node's rate or leaves under +the same parent remain permitted. We expect that for any modification +of the topology user should always create new groups and let the +kernel garbage collect the leaf-less nodes. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Mohsin Bashir +Link: https://patch.msgid.link/20260506233745.111895-1-mohsin.bashr@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 30 +++++++++++++++++++++++------- + 1 file changed, 23 insertions(+), 7 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index be9999ab62e39..e41a82241230d 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -964,15 +964,22 @@ static int __net_shaper_group(struct net_shaper_binding *binding, + int i, ret; + + if (node->handle.scope == NET_SHAPER_SCOPE_NODE) { ++ struct net_shaper *cur = NULL; ++ + new_node = node->handle.id == NET_SHAPER_ID_UNSPEC; + +- if (!new_node && !net_shaper_lookup(binding, &node->handle)) { +- /* The related attribute is not available when +- * reaching here from the delete() op. +- */ +- NL_SET_ERR_MSG_FMT(extack, "Node shaper %d:%d does not exists", +- node->handle.scope, node->handle.id); +- return -ENOENT; ++ if (!new_node) { ++ cur = net_shaper_lookup(binding, &node->handle); ++ if (!cur) { ++ /* The related attribute is not available ++ * when reaching here from the delete() op. ++ */ ++ NL_SET_ERR_MSG_FMT(extack, ++ "Node shaper %d:%d does not exist", ++ node->handle.scope, ++ node->handle.id); ++ return -ENOENT; ++ } + } + + /* When unspecified, the node parent scope is inherited from +@@ -986,6 +993,15 @@ static int __net_shaper_group(struct net_shaper_binding *binding, + return ret; + } + ++ if (cur && net_shaper_handle_cmp(&cur->parent, ++ &node->parent)) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "Cannot reparent node shaper %d:%d", ++ node->handle.scope, ++ node->handle.id); ++ return -EOPNOTSUPP; ++ } ++ + } else { + net_shaper_default_parent(&node->handle, &node->parent); + } +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-rework-the-valid-marking-again.patch b/queue-6.18/net-shaper-rework-the-valid-marking-again.patch new file mode 100644 index 0000000000..0cb93d399a --- /dev/null +++ b/queue-6.18/net-shaper-rework-the-valid-marking-again.patch @@ -0,0 +1,143 @@ +From 1e48a0479cf17e4b93b59a5dfc8bb00d1dcc648a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:13:25 -0700 +Subject: net: shaper: rework the VALID marking (again) + +From: Jakub Kicinski + +[ Upstream commit b8d7519352ba8c6df83259295d4a3bad093cae90 ] + +Recent commit changed the semantics from NOT_VALID to VALID. +I didn't realize that the flags are not stored atomically +with the entry in XArray. There's still a race of reader +observing a VALID mark for a slot, getting interrupted, +writer replacing the entry with a different one, reader +continuing, fetching the entry which is now a different +pointer than the pointer for which VALID was meant. + +The biggest consequence of this is that we may see a UAF +since net_shaper_rollback() assumed that entries without +VALID can be freed without observing RCU. + +Looks like the XArray marks are buying us nothing at this +point. Let's convert the code to an explicit valid field. +The smp_load_acquire() / smp_store_release() barriers are +marginally cleaner. + +Reported-by: Sashiko +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260515221325.1685455-3-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/net_shaper.h | 1 + + net/shaper/shaper.c | 45 ++++++++++++++++------------------------ + 2 files changed, 19 insertions(+), 27 deletions(-) + +diff --git a/include/net/net_shaper.h b/include/net/net_shaper.h +index 5c3f49b52fe96..3939b816b0011 100644 +--- a/include/net/net_shaper.h ++++ b/include/net/net_shaper.h +@@ -53,6 +53,7 @@ struct net_shaper { + + /* private: */ + u32 leaves; /* accounted only for NODE scope */ ++ bool valid; + struct rcu_head rcu; + }; + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index a01de4392eb66..f992b28e19fd6 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -306,31 +306,24 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, + parent->id = 0; + } + +-/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on +- * an entry only after the device-side configuration has completed +- * successfully (see net_shaper_commit()). Lookups and dumps must filter on +- * this mark to avoid exposing tentative entries inserted by +- * net_shaper_pre_insert() while the driver call is still in flight. +- */ +-#define NET_SHAPER_VALID XA_MARK_1 +- + static struct net_shaper * + net_shaper_lookup(struct net_shaper_binding *binding, + const struct net_shaper_handle *handle) + { + u32 index = net_shaper_handle_to_index(handle); + struct net_shaper_hierarchy *hierarchy; ++ struct net_shaper *cur; + + hierarchy = net_shaper_hierarchy_rcu(binding); +- if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, +- NET_SHAPER_VALID)) ++ if (!hierarchy) + return NULL; + +- /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is +- * valid, its contents must be visible too. +- */ +- smp_rmb(); +- return xa_load(&hierarchy->shapers, index); ++ cur = xa_load(&hierarchy->shapers, index); ++ /* Check valid before reading fields */ ++ if (!cur || !smp_load_acquire(&cur->valid)) ++ return NULL; ++ ++ return cur; + } + + /* Allocate on demand the per device shaper's hierarchy container. +@@ -444,12 +437,10 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + if (WARN_ON_ONCE(!cur)) + continue; + +- /* Successful update: drop the tentative mark +- * and update the hierarchy container. +- */ ++ /* Successful update: update the hierarchy container... */ + net_shaper_copy(cur, &shapers[i]); +- smp_wmb(); +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); ++ /* ... publish to lockless readers. */ ++ smp_store_release(&cur->valid, true); + } + xa_unlock(&hierarchy->shapers); + } +@@ -466,10 +457,10 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) + + xa_lock(&hierarchy->shapers); + xa_for_each(&hierarchy->shapers, index, cur) { +- if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) ++ if (cur->valid) + continue; + __xa_erase(&hierarchy->shapers, index); +- kfree(cur); ++ kfree_rcu(cur, rcu); + } + xa_unlock(&hierarchy->shapers); + } +@@ -882,12 +873,12 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + goto out_unlock; + + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, +- U32_MAX, NET_SHAPER_VALID)); ++ U32_MAX, XA_PRESENT)); + ctx->start_index++) { +- /* Pairs with smp_wmb() in net_shaper_commit(): the entry +- * is marked VALID, so its contents must be visible too. +- */ +- smp_rmb(); ++ /* Check valid before reading fields */ ++ if (!smp_load_acquire(&shaper->valid)) ++ continue; ++ + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-6.18/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch b/queue-6.18/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch new file mode 100644 index 0000000000..b666d7af98 --- /dev/null +++ b/queue-6.18/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch @@ -0,0 +1,41 @@ +From ec321bc1f6b39e542fb5b51cc40867971cf41174 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:59 -0700 +Subject: net: shaper: set ret to -ENOMEM when genlmsg_new() fails in + group_doit + +From: Jakub Kicinski + +[ Upstream commit 8054f85b83f42a37d482fc77ea7c9ff06a9407d9 ] + +genlmsg_new() alloc failure path in net_shaper_nl_group_doit() forgets +to set ret before jumping to error handling. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index ae19af13c2247..ee0d1f0613430 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -1277,8 +1277,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + * rollback on allocation failure. + */ + msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); +- if (!msg) ++ if (!msg) { ++ ret = -ENOMEM; + goto free_leaves; ++ } + + hierarchy = net_shaper_hierarchy_setup(binding); + if (!hierarchy) { +-- +2.53.0 + diff --git a/queue-6.18/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch b/queue-6.18/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch new file mode 100644 index 0000000000..dd3093dcd8 --- /dev/null +++ b/queue-6.18/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch @@ -0,0 +1,65 @@ +From 22c09a5ef5c88555147f814f7d0962e1b6964cf1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 15:26:40 -0700 +Subject: net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint + +From: Xiang Mei + +[ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] + +The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and +smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: + + __string(name, smc->conn.lnk->ibname) + +conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on +these paths already handles this (e.g. !conn->lnk in +SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first +sendmsg()/recvmsg() on an SMC-D socket crashes: + + Oops: general protection fault, probably for non-canonical address + KASAN: null-ptr-deref in range [...] + RIP: 0010:strlen+0x1e/0xa0 + Call Trace: + trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) + smc_rx_recvmsg (net/smc/smc_rx.c:515) + smc_recvmsg (net/smc/af_smc.c:2859) + __sys_recvfrom (net/socket.c:2315) + __x64_sys_recvfrom (net/socket.c:2326) + do_syscall_64 + +The faulting address 0x3e0 is offsetof(struct smc_link, ibname), +confirming the NULL ->lnk deref. Enabling the tracepoint requires +root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has +no capability check, and SMC-D negotiation needs no admin step on +s390 or on x86 with the loopback ISM device loaded. + +Log an empty device name for SMC-D instead of dereferencing NULL. + +Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Dust Li +Reviewed-by: Sidraya Jayagond +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/smc/smc_tracepoint.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h +index a9a6e3c1113aa..53da84f57fd6f 100644 +--- a/net/smc/smc_tracepoint.h ++++ b/net/smc/smc_tracepoint.h +@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, + __field(const void *, smc) + __field(u64, net_cookie) + __field(size_t, len) +- __string(name, smc->conn.lnk->ibname) ++ __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") + ), + + TP_fast_assign( +-- +2.53.0 + diff --git a/queue-6.18/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch b/queue-6.18/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch new file mode 100644 index 0000000000..a7c8923c09 --- /dev/null +++ b/queue-6.18/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch @@ -0,0 +1,63 @@ +From a79dbedc99d654d267c6a28a8099f03cee6e0926 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:21:38 -0700 +Subject: net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot + +From: Xiang Mei + +[ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] + +On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is +reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() +populates V2 entries starting at index 1, so when no V1 device is +selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == +NULL and ism_chid[0] == 0. + +smc_v2_determine_accepted_chid() then matches the peer's CHID against +the array starting from index 0 using the CHID alone. A malicious +peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches +the empty slot, ini->ism_selected becomes 0, and the subsequent +ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at +offsetof(struct smcd_dev, lgr_lock) == 0x68: + + BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 + Write of size 4 at addr 0000000000000068 by task exploit/144 + Call Trace: + _raw_spin_lock_bh + smc_conn_create (net/smc/smc_core.c:1997) + __smc_connect (net/smc/af_smc.c:1447) + smc_connect (net/smc/af_smc.c:1720) + __sys_connect + __x64_sys_connect + do_syscall_64 + +Require ism_dev[i] to be non-NULL before accepting a CHID match. + +Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") +Reported-by: Weiming Shi +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/smc/af_smc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c +index 6421c2e1c84de..5915fcdef743d 100644 +--- a/net/smc/af_smc.c ++++ b/net/smc/af_smc.c +@@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, + int i; + + for (i = 0; i < ini->ism_offered_cnt + 1; i++) { +- if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ++ if (ini->ism_dev[i] && ++ ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + ini->ism_selected = i; + return 0; + } +-- +2.53.0 + diff --git a/queue-6.18/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch b/queue-6.18/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch new file mode 100644 index 0000000000..74d1a844ee --- /dev/null +++ b/queue-6.18/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch @@ -0,0 +1,39 @@ +From 9d670e661fe1dffdc2b96cbb62d122856bde69e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 01:28:13 +0530 +Subject: net: ti: icssm-prueth: fix eth_ports_node leak in probe + +From: Shitalkumar Gandhi + +[ Upstream commit 6635fa84403c3a59455b66007c019a7cc632db30 ] + +The error path on of_property_read_u32() failure inside +icssm_prueth_probe() returns without putting eth_ports_node, +which was acquired before the for_each_child_of_node() loop. + +Drop it before returning. + +Fixes: 511f6c1ae093 ("net: ti: icssm-prueth: Adds ICSSM Ethernet driver") +Signed-off-by: Shitalkumar Gandhi +Link: https://patch.msgid.link/20260506195813.641610-1-shitalkumar.gandhi@cambiumnetworks.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/ti/icssm/icssm_prueth.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c +index 293b7af04263f..cc92f20685843 100644 +--- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c ++++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c +@@ -1347,6 +1347,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) + dev_err(dev, "%pOF error reading port_id %d\n", + eth_node, ret); + of_node_put(eth_node); ++ of_node_put(eth_ports_node); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-6.18/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..55c43477a3 --- /dev/null +++ b/queue-6.18/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From e57d023882ee2244708064c8a1fc22066b06d73e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index f2ea190777f0b..1ad5d0e7ae27f 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -800,11 +800,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-6.18/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-6.18/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..fad3823c6e --- /dev/null +++ b/queue-6.18/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From 4ddd5298c405721d3927b2b8c47b760043ecff1a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 1ad5d0e7ae27f..b28eb04075d1b 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -789,21 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-6.18/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-6.18/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..be1b7405de --- /dev/null +++ b/queue-6.18/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From ca7209a7695814e935e01be622d5bf7abf9ffc48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index bfaba18f0e6a8..77df9e856c2e7 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-6.18/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..143af72b6d --- /dev/null +++ b/queue-6.18/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From fcc0369d32409d652962874f47e23ddd1e247e41 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index e6f9e343b41f1..f05c79f215ea0 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 02b6501c15a5e..0fc03b07e62ae 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 9985a82555c41..8a10375d89099 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-6.18/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-6.18/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..4e38272644 --- /dev/null +++ b/queue-6.18/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From ab47ec99dfb3b5107b85cd1680a39af7dc5621f0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 7413602195525..e6f9e343b41f1 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index dacd81b12e626..02b6501c15a5e 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 0f2a8c6118d42..9985a82555c41 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index a04fc17575289..bfaba18f0e6a8 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-6.18/netfilter-nft_inner-release-local_lock-before-re-ena.patch b/queue-6.18/netfilter-nft_inner-release-local_lock-before-re-ena.patch new file mode 100644 index 0000000000..66761f26e5 --- /dev/null +++ b/queue-6.18/netfilter-nft_inner-release-local_lock-before-re-ena.patch @@ -0,0 +1,39 @@ +From 4ce9385f60c68b623da626eed73567c97e1bbffb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 11:30:49 +0200 +Subject: netfilter: nft_inner: release local_lock before re-enabling softirqs + +From: Florian Westphal + +[ Upstream commit a6cb3ff979855f7f0ee9450a947fe8f96c2ba37a ] + +Quoting sashiko: + In the error path, local_bh_enable() is called before + local_unlock_nested_bh(). + +Fixes: ba36fada9ab4 ("netfilter: nft_inner: Use nested-BH locking for nft_pcpu_tun_ctx") +Signed-off-by: Florian Westphal +Reviewed-by: Fernando Fernandez Mancera +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_inner.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c +index 1b3e7a976f560..ad08a43535b55 100644 +--- a/net/netfilter/nft_inner.c ++++ b/net/netfilter/nft_inner.c +@@ -246,8 +246,8 @@ static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt, + local_lock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); + this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx.ctx); + if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) { +- local_bh_enable(); + local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); ++ local_bh_enable(); + return false; + } + *tun_ctx = *this_cpu_tun_ctx; +-- +2.53.0 + diff --git a/queue-6.18/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-6.18/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..f2b0a955ab --- /dev/null +++ b/queue-6.18/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From 086e919f551bf17977a44a226a0a7597fb4b3051 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 77c778d84d4cb..f7916fd0e8073 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -300,6 +300,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 132b0e4a6d4df..13593391d6058 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 8b8885a73c764..c6d5b927830dd 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 97ead883e4a13..d19fce8589809 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 23c8deff8095a..663868cc5f6d6 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1789,14 +1789,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1887,7 +1879,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 595bfb492b1c1..0dea754a91209 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index db90db7057cc4..4d3b124923080 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 625a1ca13b1ba..8fc4912e790d8 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index b46a790917306..6f7afec7954bd 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 2b89adc1e5751..81175c20ccbe8 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index d585ac3c11133..08abf0df04500 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1795,14 +1795,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1894,7 +1886,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 9dcd4501fe800..cf561919bde84 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index ce2cbce9e3ed3..1a758f2bc5379 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 5be723232df8f..bb8aa3fc42b45 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 8af0f8bd036dc..923455921c1dd 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 66018b169b010..c44834d93fc79 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 1ca4fa9d249b8..2d93f189a79b9 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1538,6 +1538,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-6.18/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-6.18/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..20182f5404 --- /dev/null +++ b/queue-6.18/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From fffc92d43bb45e1418ec687474ca1613271a727d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index f7916fd0e8073..3aef60abd362c 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -299,8 +299,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index d19fce8589809..f3dadbc416a3a 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 663868cc5f6d6..f4079f0718dea 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1791,7 +1790,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 8fc4912e790d8..a0df725540251 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 08abf0df04500..dfaea4f6727ed 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1797,7 +1796,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index bb8aa3fc42b45..c2394e2c94b56 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 2d93f189a79b9..76fd0999db4a8 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1522,23 +1525,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1548,6 +1534,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1557,6 +1551,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1567,6 +1562,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2013,8 +2052,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2023,8 +2064,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-6.18/netfilter-x_tables-close-dangling-table-module-init-.patch b/queue-6.18/netfilter-x_tables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..3e461891ab --- /dev/null +++ b/queue-6.18/netfilter-x_tables-close-dangling-table-module-init-.patch @@ -0,0 +1,406 @@ +From 5aa61e0dbd265765c5b4c6dc05aeedc905cc8d04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:20 +0200 +Subject: netfilter: x_tables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] + +Similar to the previous ebtables patch: +template add exposes the table to userspace, we must do this last to +rnsure the pernet ops are set up (contain the destructors). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ + net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- + net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- + net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- + net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- + 9 files changed, 105 insertions(+), 99 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 382345567a600..370b635e3523b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { + + static int __init arptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- arptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); +- if (IS_ERR(arpfilter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(arpfilter_ops)) + return PTR_ERR(arpfilter_ops); +- } + + ret = register_pernet_subsys(&arptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ arptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(arpfilter_ops); +- return ret; ++ unregister_pernet_subsys(&arptable_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(arpfilter_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 0dea754a91209..672d7da1071d3 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { + + static int __init iptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- iptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&iptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ iptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_filter_net_ops); ++ goto err_free; + } + + return 0; ++err_free: ++ kfree(filter_ops); ++ return ret; + } + + static void __exit iptable_filter_fini(void) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 4d3b124923080..13d25d9a4610e 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -111,25 +111,26 @@ static struct pernet_operations iptable_mangle_net_ops = { + + static int __init iptable_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- iptable_mangle_table_init); +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); +- ret = PTR_ERR(mangle_ops); +- return ret; +- } ++ if (IS_ERR(mangle_ops)) ++ return PTR_ERR(mangle_ops); + + ret = register_pernet_subsys(&iptable_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ iptable_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 6f7afec7954bd..2745c22f4034d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, +- iptable_raw_table_init); +- if (ret < 0) +- return ret; +- + rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&iptable_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ++ iptable_raw_table_init); + if (ret < 0) { +- xt_unregister_template(table); +- kfree(rawtable_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 81175c20ccbe8..491894511c544 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { + + static int __init iptable_security_init(void) + { +- int ret = xt_register_template(&security_table, +- iptable_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&iptable_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ iptable_security_table_init); + if (ret < 0) { +- xt_unregister_template(&security_table); +- kfree(sectbl_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index cf561919bde84..b074fc4776764 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { + + static int __init ip6table_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- ip6table_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&ip6table_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ip6table_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(filter_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 1a758f2bc5379..e6ee036a9b2c5 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -104,25 +104,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { + + static int __init ip6table_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- ip6table_mangle_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); ++ if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); +- } + + ret = register_pernet_subsys(&ip6table_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ ip6table_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 923455921c1dd..3b161ee875bcc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, ip6table_raw_table_init); +- if (ret < 0) +- return ret; +- + /* Register hooks */ + rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&ip6table_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ip6table_raw_table_init); + if (ret < 0) { +- kfree(rawtable_ops); +- xt_unregister_template(table); +- return ret; ++ unregister_pernet_subsys(&ip6table_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index c44834d93fc79..4bd5d97b8ab65 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { + + static int __init ip6table_security_init(void) + { +- int ret = xt_register_template(&security_table, +- ip6table_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&ip6table_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ ip6table_security_table_init); + if (ret < 0) { +- kfree(sectbl_ops); +- xt_unregister_template(&security_table); +- return ret; ++ unregister_pernet_subsys(&ip6table_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/netfilter-x_tables-unregister-the-templates-first.patch b/queue-6.18/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..7d0ebb21f9 --- /dev/null +++ b/queue-6.18/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From 78243b07cc33d454e3c47ee63f4752fdb7cc0dc1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 78cd5ee24448f..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 3ab908b747951..595bfb492b1c1 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 385d945d8ebea..db90db7057cc4 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -135,8 +135,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 0e7f53964d0af..b46a790917306 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index d885443cb2679..2b89adc1e5751 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index e8992693e14a0..9dcd4501fe800 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 8dd4cd0c47bd4..ce2cbce9e3ed3 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -128,8 +128,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index fc9f6754028f2..8af0f8bd036dc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 4df14a9bae782..66018b169b010 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-6.18/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch b/queue-6.18/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch new file mode 100644 index 0000000000..4e292cc396 --- /dev/null +++ b/queue-6.18/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch @@ -0,0 +1,90 @@ +From f180c35d02a7eb2b39c341cb6c6efb4d4bd51734 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:34:00 +0100 +Subject: netfs, afs: Fix write skipping in dir/link writepages + +From: David Howells + +[ Upstream commit 9871938f99cc6cb266a77265491660e2375271f5 ] + +Fix netfs_write_single() and afs_single_writepages() to better handle a +write that would be skipped due to lock contention and WB_SYNC_NONE by +returning 1 from netfs_write_single() if it skipped and making +afs_single_writepages() skip also. If a skip occurs, the inode must be +re-marked as the VFS may have cleared the mark. + +This is really only theoretical for directories in netfs_write_single() as +the only path to that is through afs_single_writepages() that takes the +->validate_lock around it, thereby serialising it. + +Fixes: 6dd80936618c ("afs: Use netfslib for directories") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-24-dhowells@redhat.com +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/afs/dir.c | 11 ++++++++++- + fs/netfs/write_issue.c | 7 ++++++- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/fs/afs/dir.c b/fs/afs/dir.c +index 89d36e3e5c799..fa84610e0b0d9 100644 +--- a/fs/afs/dir.c ++++ b/fs/afs/dir.c +@@ -2207,7 +2207,14 @@ int afs_single_writepages(struct address_space *mapping, + /* Need to lock to prevent the folio queue and folios from being thrown + * away. + */ +- down_read(&dvnode->validate_lock); ++ if (!down_read_trylock(&dvnode->validate_lock)) { ++ if (wbc->sync_mode == WB_SYNC_NONE) { ++ /* The VFS will have undirtied the inode. */ ++ netfs_single_mark_inode_dirty(&dvnode->netfs.inode); ++ return 0; ++ } ++ down_read(&dvnode->validate_lock); ++ } + + if (is_dir ? + test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : +@@ -2215,6 +2222,8 @@ int afs_single_writepages(struct address_space *mapping, + iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, + i_size_read(&dvnode->netfs.inode)); + ret = netfs_writeback_single(mapping, wbc, &iter); ++ if (ret == 1) ++ ret = 0; /* Skipped write due to lock conflict. */ + } + + up_read(&dvnode->validate_lock); +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 9bf05099155dc..03d170b9022b7 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -829,6 +829,9 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + * + * Write a monolithic, non-pagecache object back to the server and/or + * the cache. ++ * ++ * Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_NONE; ++ * or a negative error code. + */ + int netfs_writeback_single(struct address_space *mapping, + struct writeback_control *wbc, +@@ -845,8 +848,10 @@ int netfs_writeback_single(struct address_space *mapping, + + if (!mutex_trylock(&ictx->wb_lock)) { + if (wbc->sync_mode == WB_SYNC_NONE) { ++ /* The VFS will have undirtied the inode. */ ++ netfs_single_mark_inode_dirty(&ictx->inode); + netfs_stat(&netfs_n_wb_lock_skip); +- return 0; ++ return 1; + } + netfs_stat(&netfs_n_wb_lock_wait); + mutex_lock(&ictx->wb_lock); +-- +2.53.0 + diff --git a/queue-6.18/netfs-defer-the-emission-of-trace_netfs_folio.patch b/queue-6.18/netfs-defer-the-emission-of-trace_netfs_folio.patch new file mode 100644 index 0000000000..205d194e24 --- /dev/null +++ b/queue-6.18/netfs-defer-the-emission-of-trace_netfs_folio.patch @@ -0,0 +1,116 @@ +From 1b67f05070ca7d492c9b0a1f22439720a2bb008b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:49 +0100 +Subject: netfs: Defer the emission of trace_netfs_folio() + +From: David Howells + +[ Upstream commit daeb443b92817021c1234e8eded219e164b7c35d ] + +Change netfs_perform_write() to keep the netfs_folio trace value in a +variable and emit it later to make it easier to choose the value displayed. +This is a prerequisite for a subsequent patch. + +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten") +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 09394ac2c180d..1fa13c2629a73 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -150,6 +150,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + do { ++ enum netfs_folio_trace trace; + struct netfs_folio *finfo; + struct netfs_group *group; + unsigned long long fpos; +@@ -223,7 +224,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_folio_is_uptodate); ++ trace = netfs_folio_is_uptodate; + goto copied; + } + +@@ -239,7 +240,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + folio_zero_segment(folio, offset + copied, flen); + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_modify_and_clear); ++ trace = netfs_modify_and_clear; + goto copied; + } + +@@ -257,7 +258,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_whole_folio_modify); ++ trace = netfs_whole_folio_modify; + goto copied; + } + +@@ -284,7 +285,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_just_prefetch); ++ trace = netfs_just_prefetch; + goto copied; + } + +@@ -298,7 +299,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (offset == 0 && copied == flen) { + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_streaming_filled_page); ++ trace = netfs_streaming_filled_page; + goto copied; + } + +@@ -313,7 +314,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len = copied; + folio_attach_private(folio, (void *)((unsigned long)finfo | + NETFS_FOLIO_INFO)); +- trace_netfs_folio(folio, netfs_streaming_write); ++ trace = netfs_streaming_write; + goto copied; + } + +@@ -333,9 +334,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + folio_detach_private(folio); + folio_mark_uptodate(folio); + kfree(finfo); +- trace_netfs_folio(folio, netfs_streaming_cont_filled_page); ++ trace = netfs_streaming_cont_filled_page; + } else { +- trace_netfs_folio(folio, netfs_streaming_write_cont); ++ trace = netfs_streaming_write_cont; + } + goto copied; + } +@@ -351,6 +352,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + continue; + + copied: ++ trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); + + /* Update the inode size if we moved the EOF marker */ +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch b/queue-6.18/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch new file mode 100644 index 0000000000..9c711ecbdf --- /dev/null +++ b/queue-6.18/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch @@ -0,0 +1,342 @@ +From 77bcf9ab362ac83e21f2cd973afb4802eb1e3b46 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:38 +0100 +Subject: netfs: Fix cancellation of a DIO and single read subrequests + +From: David Howells + +[ Upstream commit 6f0f7ac1915abc0d202f0eb4b003a6548a5ba60d ] + +When the preparation of a new subrequest for a read fails, if the +subrequest has already been added to the stream->subrequests list, it can't +simply be put and abandoned as the collector may see it. Also, if it +hasn't been queued yet, it has two outstanding refs that both need to be +put. Both DIO read and single-read dispatch fail at this; further, both +differ in the order they do things to the way buffered read works. + +Fix cancellation of both DIO-read and single-read subrequests that failed +preparation by the following steps: + + (1) Harmonise all three reads (buffered, dio, single) to queue the subreq + before prepping it. + + (2) Make all three call netfs_queue_read() to do the queuing. + + (3) Set NETFS_RREQ_ALL_QUEUED independently of the queuing as we don't + know the length of the subreq at this point. + + (4) In all cases, set the error and NETFS_SREQ_FAILED flag on the subreq + and then call netfs_read_subreq_terminated() to deal with it. This + will pass responsibility off to the collector for dealing with it. + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-2-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 34 +++++++++++++------------------- + fs/netfs/direct_read.c | 42 +++++++++++++--------------------------- + fs/netfs/internal.h | 3 +++ + fs/netfs/read_collect.c | 11 +++++++++++ + fs/netfs/read_single.c | 23 ++++++++++------------ + 5 files changed, 50 insertions(+), 63 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 88361e8c70961..10b13924ed543 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq, + netfs_cache_read_terminated, subreq); + } + +-static void netfs_queue_read(struct netfs_io_request *rreq, +- struct netfs_io_subrequest *subreq, +- bool last_subreq) ++void netfs_queue_read(struct netfs_io_request *rreq, ++ struct netfs_io_subrequest *subreq) + { + struct netfs_io_stream *stream = &rreq->io_streams[0]; + +@@ -178,11 +177,6 @@ static void netfs_queue_read(struct netfs_io_request *rreq, + } + } + +- if (last_subreq) { +- smp_wmb(); /* Write lists before ALL_QUEUED. */ +- set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); +- } +- + spin_unlock(&rreq->lock); + } + +@@ -233,6 +227,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + subreq->start = start; + subreq->len = size; + ++ netfs_queue_read(rreq, subreq); ++ + source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size); + subreq->source = source; + if (source == NETFS_DOWNLOAD_FROM_SERVER) { +@@ -253,6 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + rreq->debug_id, subreq->debug_index, + subreq->len, size, + subreq->start, ictx->zero_point, rreq->i_size); ++ netfs_cancel_read(subreq, ret); + break; + } + subreq->len = len; +@@ -261,12 +258,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + if (rreq->netfs_ops->prepare_read) { + ret = rreq->netfs_ops->prepare_read(subreq); + if (ret < 0) { +- subreq->error = ret; +- /* Not queued - release both refs. */ +- netfs_put_subrequest(subreq, +- netfs_sreq_trace_put_cancel); +- netfs_put_subrequest(subreq, +- netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } + trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); +@@ -289,23 +281,23 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + + pr_err("Unexpected read source %u\n", source); + WARN_ON_ONCE(1); ++ netfs_cancel_read(subreq, ret); + break; + + issue: + slice = netfs_prepare_read_iterator(subreq, ractl); + if (slice < 0) { + ret = slice; +- subreq->error = ret; +- trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); +- /* Not queued - release both refs. */ +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } +- size -= slice; + start += slice; ++ size -= slice; ++ if (size <= 0) { ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); ++ } + +- netfs_queue_read(rreq, subreq, size <= 0); + netfs_issue_read(rreq, subreq); + cond_resched(); + } while (size > 0); +diff --git a/fs/netfs/direct_read.c b/fs/netfs/direct_read.c +index f72e6da88cca7..6a8fb0d55e040 100644 +--- a/fs/netfs/direct_read.c ++++ b/fs/netfs/direct_read.c +@@ -45,12 +45,11 @@ static void netfs_prepare_dio_read_iterator(struct netfs_io_subrequest *subreq) + * Perform a read to a buffer from the server, slicing up the region to be read + * according to the network rsize. + */ +-static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) ++static void netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + { +- struct netfs_io_stream *stream = &rreq->io_streams[0]; + unsigned long long start = rreq->start; + ssize_t size = rreq->len; +- int ret = 0; ++ int ret; + + do { + struct netfs_io_subrequest *subreq; +@@ -58,7 +57,10 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + + subreq = netfs_alloc_subrequest(rreq); + if (!subreq) { +- ret = -ENOMEM; ++ /* Stash the error in the request if there's not ++ * already an error set. ++ */ ++ cmpxchg(&rreq->error, 0, -ENOMEM); + break; + } + +@@ -66,25 +68,13 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + subreq->start = start; + subreq->len = size; + +- __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); +- +- spin_lock(&rreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); +- if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { +- if (!stream->active) { +- stream->collected_to = subreq->start; +- /* Store list pointers before active flag */ +- smp_store_release(&stream->active, true); +- } +- } +- trace_netfs_sreq(subreq, netfs_sreq_trace_added); +- spin_unlock(&rreq->lock); ++ netfs_queue_read(rreq, subreq); + + netfs_stat(&netfs_n_rh_download); + if (rreq->netfs_ops->prepare_read) { + ret = rreq->netfs_ops->prepare_read(subreq); + if (ret < 0) { +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } + } +@@ -113,8 +103,6 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + netfs_wake_collector(rreq); + } +- +- return ret; + } + + /* +@@ -137,21 +125,17 @@ static ssize_t netfs_unbuffered_read(struct netfs_io_request *rreq, bool sync) + // TODO: Use bounce buffer if requested + + inode_dio_begin(rreq->inode); ++ netfs_dispatch_unbuffered_reads(rreq); + +- ret = netfs_dispatch_unbuffered_reads(rreq); +- +- if (!rreq->submitted) { +- netfs_put_request(rreq, netfs_rreq_trace_put_no_submit); +- inode_dio_end(rreq->inode); +- ret = 0; +- goto out; +- } ++ /* The collector will get run, even if we don't manage to submit any ++ * subreqs, so we shouldn't call inode_dio_end() here. ++ */ + + if (sync) + ret = netfs_wait_for_read(rreq); + else + ret = -EIOCBQUEUED; +-out: ++ + _leave(" = %zd", ret); + return ret; + } +diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h +index d436e20d34185..645996ecfc803 100644 +--- a/fs/netfs/internal.h ++++ b/fs/netfs/internal.h +@@ -23,6 +23,8 @@ + /* + * buffered_read.c + */ ++void netfs_queue_read(struct netfs_io_request *rreq, ++ struct netfs_io_subrequest *subreq); + void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); + int netfs_prefetch_for_write(struct file *file, struct folio *folio, + size_t offset, size_t len); +@@ -108,6 +110,7 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq, + */ + bool netfs_read_collection(struct netfs_io_request *rreq); + void netfs_read_collection_worker(struct work_struct *work); ++void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); + void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); + + /* +diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c +index e5f6665b3341e..d2d902f466271 100644 +--- a/fs/netfs/read_collect.c ++++ b/fs/netfs/read_collect.c +@@ -575,6 +575,17 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) + } + EXPORT_SYMBOL(netfs_read_subreq_terminated); + ++/* ++ * Cancel a read subrequest due to preparation failure. ++ */ ++void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error) ++{ ++ trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); ++ subreq->error = error; ++ __set_bit(NETFS_SREQ_FAILED, &subreq->flags); ++ netfs_read_subreq_terminated(subreq); ++} ++ + /* + * Handle termination of a read from the cache. + */ +diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c +index 9d48ced80d1fa..cb422de66d0c5 100644 +--- a/fs/netfs/read_single.c ++++ b/fs/netfs/read_single.c +@@ -89,7 +89,6 @@ static void netfs_single_read_cache(struct netfs_io_request *rreq, + */ + static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + { +- struct netfs_io_stream *stream = &rreq->io_streams[0]; + struct netfs_io_subrequest *subreq; + int ret = 0; + +@@ -102,14 +101,7 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + subreq->len = rreq->len; + subreq->io_iter = rreq->buffer.iter; + +- __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); +- +- spin_lock(&rreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); +- trace_netfs_sreq(subreq, netfs_sreq_trace_added); +- /* Store list pointers before active flag */ +- smp_store_release(&stream->active, true); +- spin_unlock(&rreq->lock); ++ netfs_queue_read(rreq, subreq); + + netfs_single_cache_prepare_read(rreq, subreq); + switch (subreq->source) { +@@ -121,10 +113,14 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + goto cancel; + } + ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + rreq->netfs_ops->issue_read(subreq); + rreq->submitted += subreq->len; + break; + case NETFS_READ_FROM_CACHE: ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + trace_netfs_sreq(subreq, netfs_sreq_trace_submit); + netfs_single_read_cache(rreq, subreq); + rreq->submitted += subreq->len; +@@ -134,14 +130,15 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + pr_warn("Unexpected single-read source %u\n", subreq->source); + WARN_ON_ONCE(true); + ret = -EIO; +- break; ++ goto cancel; + } + +- smp_wmb(); /* Write lists before ALL_QUEUED. */ +- set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + return ret; + cancel: +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); ++ netfs_wake_collector(rreq); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch b/queue-6.18/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch new file mode 100644 index 0000000000..48181053f7 --- /dev/null +++ b/queue-6.18/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch @@ -0,0 +1,82 @@ +From 55bd2db9c272ffbbbbbf66c6f07402c680db01df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:54 +0100 +Subject: netfs: Fix early put of sink folio in netfs_read_gaps() + +From: David Howells + +[ Upstream commit 3e5dd91b87a8b1450217b56a336bee315f40da7d ] + +Fix netfs_read_gaps() to release the sink page it uses after waiting for +the request to complete. The way the sink page is used is that an +ITER_BVEC-class iterator is created that has the gaps from the target folio +at either end, but has the sink page tiled over the middle so that a single +read op can fill in both gaps. + +The bug was found by KASAN detecting a UAF on the generic/075 xfstest in +the cifsd kernel thread that handles reception of data from the TCP socket: + + BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 + Write of size 885 at addr ffff888107f92000 by task cifsd/1285 + CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) + Call Trace: + dump_stack_lvl+0x5d/0x80 + print_report+0x17f/0x4f1 + kasan_report+0x100/0x1e0 + kasan_check_range+0x10f/0x1e0 + __asan_memcpy+0x3c/0x60 + _copy_to_iter+0x48a/0xa20 + __skb_datagram_iter+0x2c9/0x430 + skb_copy_datagram_iter+0x6e/0x160 + tcp_recvmsg_locked+0xce0/0x1130 + tcp_recvmsg+0xeb/0x300 + inet_recvmsg+0xcf/0x3a0 + sock_recvmsg+0xea/0x100 + cifs_readv_from_socket+0x3a6/0x4d0 [cifs] + cifs_read_iter_from_socket+0xdd/0x130 [cifs] + cifs_readv_receive+0xaad/0xb10 [cifs] + cifs_demultiplex_thread+0x1148/0x1740 [cifs] + kthread+0x1cf/0x210 + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Reported-by: Steve French +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 3531c19eea97a..762ff928bc878 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -456,9 +456,6 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + + netfs_read_to_pagecache(rreq, NULL); + +- if (sink) +- folio_put(sink); +- + ret = netfs_wait_for_read(rreq); + if (ret >= 0) { + if (group) +@@ -470,6 +467,9 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + flush_dcache_folio(folio); + folio_mark_uptodate(folio); + } ++ ++ if (sink) ++ folio_put(sink); + folio_unlock(folio); + netfs_put_request(rreq, netfs_rreq_trace_put_return); + return ret < 0 ? ret : 0; +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch b/queue-6.18/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch new file mode 100644 index 0000000000..38a3efaab2 --- /dev/null +++ b/queue-6.18/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch @@ -0,0 +1,307 @@ +From 2b63201cb71aba730a1a5b541d599efa8f408fa7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:58 +0100 +Subject: netfs: Fix folio->private handling in netfs_perform_write() + +From: David Howells + +[ Upstream commit ccde2ac757c713535b224233a296de40efe5212d ] + +Under some circumstances, netfs_perform_write() doesn't correctly +manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing +to a group and pointing to a netfs_folio struct, leading to potential +multiple attachments of private data with associated folio ref leaks and +also leaks of netfs_folio structs or netfs_group refs. + +Fix this by consolidating the place at which a folio is marked uptodate in +one place and having that look at what's attached to folio->private and +decide how to clean it up and then set the new group. Also, the content +shouldn't be flushed if group is NULL, even if a group is specified in the +netfs_group parameter, as that would be the case for a new folio. A +filesystem should always specify netfs_group or never specify netfs_group. + +The Sashiko auto-review tool noted that it was theoretically possible that +the fpos >= ctx->zero_point section might leak if it modified a streaming +write folio. This is unlikely, but with a network filesystem, third party +changes can happen. It also pointed out that __netfs_set_group() would +leak if called multiple times on the same folio from the "whole folio +modify section". + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 134 +++++++++++++++++++++-------------- + include/trace/events/netfs.h | 1 + + 2 files changed, 82 insertions(+), 53 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 60215a7723574..dd0ce7b769ce0 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -13,24 +13,6 @@ + #include + #include "internal.h" + +-static void __netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- if (netfs_group) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +-} +- +-static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- void *priv = folio_get_private(folio); +- +- if (unlikely(priv != netfs_group)) { +- if (netfs_group && (!priv || priv == NETFS_FOLIO_COPY_TO_CACHE)) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +- else if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) +- folio_detach_private(folio); +- } +-} +- + /* + * Grab a folio for writing and lock it. Attempt to allocate as large a folio + * as possible to hold as much of the remaining length as possible in one go. +@@ -158,6 +140,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + size_t offset; /* Offset into pagecache folio */ + size_t part; /* Bytes to write to folio */ + size_t copied; /* Bytes copied from user */ ++ void *priv; + + offset = pos & (max_chunk - 1); + part = min(max_chunk - offset, iov_iter_count(iter)); +@@ -203,6 +186,25 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto error_folio_unlock; + } + ++ finfo = netfs_folio_info(folio); ++ group = netfs_folio_group(folio); ++ ++ /* If the requested group differs from the group set on the ++ * page, then we need to flush out the folio if it has a group ++ * set (ie. is non-NULL). Note that COPY_TO_CACHE is a special ++ * case, being a netfs annotation rather than an actual group. ++ * ++ * The filesystem isn't permitted to mix writes with groups and ++ * writes without groups as the NULL group is used to indicate ++ * that no group is set. ++ */ ++ if (unlikely(group != netfs_group) && ++ group != NETFS_FOLIO_COPY_TO_CACHE && ++ group) { ++ WARN_ON_ONCE(!netfs_group); ++ goto flush_content; ++ } ++ + /* Decide how we should modify a folio. We might be attempting + * to do write-streaming, as we don't want to a local RMW cycle + * if we can avoid it. If we're doing local caching or content +@@ -210,22 +212,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + * file is open readably, then we let ->read_folio() fill in + * the gaps. + */ +- finfo = netfs_folio_info(folio); +- group = netfs_folio_group(folio); +- +- if (unlikely(group != netfs_group) && +- group != NETFS_FOLIO_COPY_TO_CACHE) +- goto flush_content; +- + if (folio_test_uptodate(folio)) { + if (mapping_writably_mapped(mapping)) + flush_dcache_folio(folio); + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_folio_is_uptodate; +- goto copied; ++ goto copied_uptodate; + } + + /* If the page is above the zero-point then we assume that the +@@ -238,24 +232,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + folio_zero_segment(folio, offset + copied, flen); +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_modify_and_clear; +- goto copied; ++ if (finfo) ++ trace = netfs_modify_and_clear_rm_finfo; ++ else ++ trace = netfs_modify_and_clear; ++ goto mark_uptodate; + } + + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (likely(copied == part)) { +- if (finfo) { ++ if (finfo) + trace = netfs_whole_folio_modify_filled; +- goto folio_now_filled; +- } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; +- goto copied; ++ else ++ trace = netfs_whole_folio_modify; ++ goto mark_uptodate; + } + if (copied == 0) + goto copy_failed; +@@ -273,7 +265,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += finfo->dirty_offset; + if (finfo->dirty_len == flen) { + trace = netfs_whole_folio_modify_filled_efault; +- goto folio_now_filled; ++ goto mark_uptodate; + } + if (copied > finfo->dirty_len) + finfo->dirty_len = copied; +@@ -301,11 +293,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_just_prefetch; +- goto copied; ++ goto copied_uptodate; + } + ++ /* Do a streaming write on a folio that has nothing in it yet. */ + if (!finfo) { + ret = -EIO; + if (WARN_ON(folio_get_private(folio))) +@@ -314,10 +306,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + if (offset == 0 && copied == flen) { +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); + trace = netfs_streaming_filled_page; +- goto copied; ++ goto mark_uptodate; + } + + finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); +@@ -346,7 +336,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { + trace = netfs_streaming_cont_filled_page; +- goto folio_now_filled; ++ goto mark_uptodate; + } + trace = netfs_streaming_write_cont; + goto copied; +@@ -362,13 +352,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + +- folio_now_filled: +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); ++ /* Mark a folio as being up to data when we've filled it ++ * completely. If the folio has a group attached, then it must ++ * be the same group, otherwise we should have flushed it out ++ * above. We have to get rid of the netfs_folio struct if ++ * there was one. ++ */ ++ mark_uptodate: + folio_mark_uptodate(folio); +- kfree(finfo); ++ ++ copied_uptodate: ++ priv = folio_get_private(folio); ++ if (likely(priv == netfs_group)) { ++ /* Already set correctly; no change required. */ ++ } else if (priv == NETFS_FOLIO_COPY_TO_CACHE) { ++ if (!netfs_group) ++ folio_detach_private(folio); ++ else ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ } else if (!priv) { ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ } else { ++ WARN_ON_ONCE(!finfo); ++ if (netfs_group) ++ /* finfo->netfs_group has a ref */ ++ folio_change_private(folio, netfs_group); ++ else ++ folio_detach_private(folio); ++ kfree(finfo); ++ } ++ + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +@@ -531,6 +544,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + struct inode *inode = file_inode(file); + struct netfs_inode *ictx = netfs_inode(inode); + vm_fault_t ret = VM_FAULT_NOPAGE; ++ void *priv; + int err; + + _enter("%lx", folio->index); +@@ -551,7 +565,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + } + + group = netfs_folio_group(folio); +- if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) { ++ if (group && ++ group != netfs_group && ++ group != NETFS_FOLIO_COPY_TO_CACHE) { + folio_unlock(folio); + err = filemap_fdatawrite_range(mapping, + folio_pos(folio), +@@ -573,7 +589,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus); + else + trace_netfs_folio(folio, netfs_folio_trace_mkwrite); +- netfs_set_group(folio, netfs_group); ++ ++ priv = folio_get_private(folio); ++ if (priv != netfs_group) { ++ if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_detach_private(folio); ++ else if (netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ else if (netfs_group && !priv) ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ else ++ WARN_ON_ONCE(1); ++ } ++ + file_update_time(file); + set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); + if (ictx->ops->post_modify) +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index db045135406c9..3fe3980902c24 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -181,6 +181,7 @@ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ ++ EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ + EM(netfs_flush_content, "flush") \ +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch b/queue-6.18/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch new file mode 100644 index 0000000000..337f8798a8 --- /dev/null +++ b/queue-6.18/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch @@ -0,0 +1,44 @@ +From a27b4314e7771c498a7762186fc5505f3c2a9b3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:55 +0100 +Subject: netfs: Fix leak of request in netfs_write_begin() error handling + +From: David Howells + +[ Upstream commit 5046a34f0643441f05b0253ea64e1a3af87efe14 ] + +Fix netfs_write_begin() to not leak our ref on the request in the event +that we get an error from netfs_wait_for_read(). + +Fixes: 4090b31422a6 ("netfs: Add a function to consolidate beginning a read") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-19-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 762ff928bc878..085cd392bf5ac 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -686,9 +686,9 @@ int netfs_write_begin(struct netfs_inode *ctx, + + netfs_read_to_pagecache(rreq, NULL); + ret = netfs_wait_for_read(rreq); ++ netfs_put_request(rreq, netfs_rreq_trace_put_return); + if (ret < 0) + goto error; +- netfs_put_request(rreq, netfs_rreq_trace_put_return); + + have_folio: + ret = folio_wait_private_2_killable(folio); +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch b/queue-6.18/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch new file mode 100644 index 0000000000..36d8c35261 --- /dev/null +++ b/queue-6.18/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch @@ -0,0 +1,128 @@ +From 1653482cf7a41091fe4d2204e137183725809d8f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:48 +0100 +Subject: netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes + gone + +From: David Howells + +[ Upstream commit 156ac2ec2ee77c44c4eb7439d6d165247ba12247 ] + +If a streaming write is made, this will leave the relevant modified folio +in a not-uptodate, but dirty state with a netfs_folio struct hung off of +folio->private indicating the dirty range. Subsequently truncating the +file such that the dirty data in the folio is removed, but the first part +of the folio theoretically remains will cause the netfs_folio struct to be +discarded... but will leave the dirty flag set. + +If the folio is then read via mmap(), netfs_read_folio() will see that the +page is dirty and jump to netfs_read_gaps() to fill in the missing bits. +netfs_read_gaps(), however, expects there to be a netfs_folio struct +present and can oops because truncate removed it. + +Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the +event that all the dirty data in the folio is erased (as nfs does). + +Also add some tracepoints to log modifications to a dirty page. + +This can be reproduced with something like: + + dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1 + umount /xfstest.test + mount /xfstest.test + xfs_io -c "w 0xbbbf 0xf96c" \ + -c "truncate 0xbbbf" \ + -c "mmap -r 0xb000 0x11000" \ + -c "mr 0xb000 0x11000" \ + /xfstest.test/foo + +with fscaching disabled (otherwise streaming writes are suppressed) and a +change to netfs_perform_write() to disallow streaming writes if the fd is +open O_RDWR: + + if (//(file->f_mode & FMODE_READ) || <--- comment this out + netfs_is_cache_enabled(ctx)) { + +It should be reproducible even without this change, but if prevents the +above trivial xfs_io command from reproducing it. + +Note that the initial dd is important: the file must start out sufficiently +large that the zero-point logic doesn't just clear the gaps because it +knows there's nothing in the file to read yet. Unmounting and mounting is +needed to clear the pagecache (there are other ways to do that that may +also work). + +This was initially reproduced with the generic/522 xfstest on some patches +that remove the FMODE_READ restriction. + +Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write") +Reported-by: Marc Dionne +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 6 +++++- + include/trace/events/netfs.h | 4 ++++ + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 486166460e177..eb309bef72608 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -256,6 +256,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; + finfo->dirty_offset = offset; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } + +@@ -264,12 +265,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + */ + if (iend >= fend) { + finfo->dirty_len = offset - fstart; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_tail); + return; + } + + /* A partial write was split. The caller has already zeroed + * it, so just absorb the hole. + */ ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_middle); + } + return; + +@@ -277,8 +280,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + netfs_put_group(netfs_folio_group(folio)); + folio_detach_private(folio); + folio_clear_uptodate(folio); ++ folio_cancel_dirty(folio); + kfree(finfo); +- return; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_all); + } + EXPORT_SYMBOL(netfs_invalidate_folio); + +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index cbe28211106c5..88d814ba1e697 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -194,6 +194,10 @@ + EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ + EM(netfs_folio_trace_end_copy, "end-copy") \ + EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ ++ EM(netfs_folio_trace_invalidate_all, "inval-all") \ ++ EM(netfs_folio_trace_invalidate_front, "inval-front") \ ++ EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ ++ EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ + EM(netfs_folio_trace_kill, "kill") \ + EM(netfs_folio_trace_kill_cc, "kill-cc") \ + EM(netfs_folio_trace_kill_g, "kill-g") \ +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch b/queue-6.18/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch new file mode 100644 index 0000000000..3149781c2e --- /dev/null +++ b/queue-6.18/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch @@ -0,0 +1,44 @@ +From b3f1c53def7314a2e2c63c509ce60808a061b402 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:59 +0100 +Subject: netfs: Fix netfs_read_folio() to wait on writeback + +From: David Howells + +[ Upstream commit ded0c6f1606061148c202825f7e53d711f9f84cf ] + +Fix netfs_read_folio() to wait for an ongoing writeback to complete so that +it can trust the dirty flag and whatever is attached to folio->private +(folio->private may get cleaned up by the collector before it clears the +writeback flag). + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-23-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index ebaabec376326..fab3181c7f869 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -502,6 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) + struct netfs_inode *ctx = netfs_inode(mapping->host); + int ret; + ++ folio_wait_writeback(folio); ++ + if (folio_test_dirty(folio)) + return netfs_read_gaps(file, folio); + +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch b/queue-6.18/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch new file mode 100644 index 0000000000..1f38fdc488 --- /dev/null +++ b/queue-6.18/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch @@ -0,0 +1,44 @@ +From 2a102f7a47a949d18b74545cfb2b077c1937b15c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:41 +0100 +Subject: netfs: Fix netfs_read_to_pagecache() to pause on subreq failure + +From: David Howells + +[ Upstream commit 8a8c0cfdf4658fc5b295b7fc87be56e0d76741f4 ] + +Fix netfs_read_to_pagecache() so that it pauses the generation of new +subrequests if an already-issued subrequest fails. + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-5-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 10b13924ed543..e6157122da3a4 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -299,6 +299,11 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + } + + netfs_issue_read(rreq, subreq); ++ ++ if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) ++ netfs_wait_for_paused_read(rreq); ++ if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) ++ break; + cond_resched(); + } while (size > 0); + +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch b/queue-6.18/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch new file mode 100644 index 0000000000..06b522f193 --- /dev/null +++ b/queue-6.18/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch @@ -0,0 +1,80 @@ +From 8cc863f8be9757cc819109068243b8f8577757ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:47 +0100 +Subject: netfs: Fix overrun check in netfs_extract_user_iter() + +From: David Howells + +[ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ] + +Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills +pages[], then those pages don't get included in the iterator constructed at +the end of the function. If there was an overfill, memory corruption has +already happened. + +Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into a BVEC iterator") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/iterator.c | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c +index 429e4396e1b00..b375567e0520e 100644 +--- a/fs/netfs/iterator.c ++++ b/fs/netfs/iterator.c +@@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + break; + } + +- if (ret > count) { +- pr_err("get_pages rc=%zd more than %zu\n", ret, count); ++ if (WARN(ret > count, ++ "%s: extract_pages overrun %zd > %zu bytes\n", ++ __func__, ret, count)) { ++ ret = -EIO; + break; + } + +- count -= ret; +- ret += offset; +- cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE); +- +- if (npages + cur_npages > max_pages) { +- pr_err("Out of bvec array capacity (%u vs %u)\n", +- npages + cur_npages, max_pages); ++ cur_npages = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ if (WARN(cur_npages > max_pages - npages, ++ "%s: extract_pages overrun %u > %u pages\n", ++ __func__, npages + cur_npages, max_pages)) { ++ ret = -EIO; + break; + } + ++ count -= ret; ++ ret += offset; ++ + for (i = 0; i < cur_npages; i++) { + len = ret > PAGE_SIZE ? PAGE_SIZE : ret; + bvec_set_page(bv + npages + i, *pages++, len - offset, offset); +@@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + npages += cur_npages; + } + ++ /* Note: Don't try to clean up after EIO. Either we got no pages, so ++ * nothing to clean up, or we got a buffer overrun, memory corruption ++ * and can't trust the stuff in the buffer (a WARN was emitted). ++ */ ++ + if (ret < 0 && (ret == -ENOMEM || npages == 0)) { + for (i = 0; i < npages; i++) + unpin_user_page(bv[i].bv_page); +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-partial-invalidation-of-streaming-write-fo.patch b/queue-6.18/netfs-fix-partial-invalidation-of-streaming-write-fo.patch new file mode 100644 index 0000000000..fad19e82d2 --- /dev/null +++ b/queue-6.18/netfs-fix-partial-invalidation-of-streaming-write-fo.patch @@ -0,0 +1,49 @@ +From e64ead2ae68266dcd51228cf87d475f03f7449b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:57 +0100 +Subject: netfs: Fix partial invalidation of streaming-write folio + +From: David Howells + +[ Upstream commit 6d91acc7fb85d33ea58fca9b964a32a453937f4b ] + +In netfs_invalidate_folio(), if the region of a partial invalidation +overlaps the front (but not all) of a dirty write cached in a streaming +write page (dirty, but not uptodate, with the dirty region tracked by a +netfs_folio struct), the function modifies the dirty region - but +incorrectly as it moves the region forward by setting the start to the +start, not the end, of the invalidation region. + +Fix this by setting finfo->dirty_offset to the end of the invalidation +region (iend). + +Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netfs_inval_folio()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index eb309bef72608..1109ac3791281 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -255,7 +255,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + goto erase_completely; + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; +- finfo->dirty_offset = offset; ++ finfo->dirty_offset = iend; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-potential-deadlock-in-write-through-mode.patch b/queue-6.18/netfs-fix-potential-deadlock-in-write-through-mode.patch new file mode 100644 index 0000000000..4da8299c6c --- /dev/null +++ b/queue-6.18/netfs-fix-potential-deadlock-in-write-through-mode.patch @@ -0,0 +1,119 @@ +From eefc7a7081a648defbbb752e4bdb3e250a869262 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:51 +0100 +Subject: netfs: Fix potential deadlock in write-through mode + +From: David Howells + +[ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ] + +Fix netfs_advance_writethrough() to always unlock the supplied folio and to +mark it dirty if it isn't yet written to the end. Unfortunately, it can't +be marked for writeback until the folio is done with as that may cause a +deadlock against mmapped reads and writes. + +Even though it has been marked dirty, premature writeback can't occur as +the caller is holding both inode->i_rwsem (which will prevent concurrent +truncation, fallocation, DIO and other writes) and ictx->wb_lock (which +will cause flushing to wait and writeback to skip or wait). + +Note that this may be easier to deal with once the queuing of folios is +split from the generation of subrequests. + +Fixes: 288ace2f57c9 ("netfs: New writeback implementation") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/write_issue.c | 39 +++++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 14 deletions(-) + +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 2db688f941251..9bf05099155dc 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -413,12 +413,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, + if (streamw) + netfs_issue_write(wreq, cache); + +- /* Flip the page to the writeback state and unlock. If we're called +- * from write-through, then the page has already been put into the wb +- * state. +- */ +- if (wreq->origin == NETFS_WRITEBACK) +- folio_start_writeback(folio); ++ folio_start_writeback(folio); + folio_unlock(folio); + + if (fgroup == NETFS_FOLIO_COPY_TO_CACHE) { +@@ -646,29 +641,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c + struct folio *folio, size_t copied, bool to_page_end, + struct folio **writethrough_cache) + { ++ int ret; ++ + _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u", + wreq->debug_id, wreq->buffer.iter.count, wreq->wsize, copied, to_page_end); + +- if (!*writethrough_cache) { +- if (folio_test_dirty(folio)) +- /* Sigh. mmap. */ +- folio_clear_dirty_for_io(folio); ++ /* The folio is locked. */ + ++ if (*writethrough_cache != folio) { ++ if (*writethrough_cache) { ++ /* Did the folio get moved? */ ++ folio_put(*writethrough_cache); ++ *writethrough_cache = NULL; ++ } + /* We can make multiple writes to the folio... */ +- folio_start_writeback(folio); + if (wreq->len == 0) + trace_netfs_folio(folio, netfs_folio_trace_wthru); + else + trace_netfs_folio(folio, netfs_folio_trace_wthru_plus); + *writethrough_cache = folio; ++ folio_get(folio); + } + + wreq->len += copied; +- if (!to_page_end) ++ ++ if (!to_page_end) { ++ folio_mark_dirty(folio); ++ folio_unlock(folio); + return 0; ++ } + ++ ret = netfs_write_folio(wreq, wbc, folio); ++ folio_put(*writethrough_cache); + *writethrough_cache = NULL; +- return netfs_write_folio(wreq, wbc, folio); ++ wreq->submitted = wreq->len; ++ return ret; + } + + /* +@@ -682,8 +689,12 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c + + _enter("R=%x", wreq->debug_id); + +- if (writethrough_cache) ++ if (writethrough_cache) { ++ folio_lock(writethrough_cache); + netfs_write_folio(wreq, wbc, writethrough_cache); ++ folio_put(writethrough_cache); ++ wreq->submitted = wreq->len; ++ } + + netfs_end_issue_write(wreq); + +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch b/queue-6.18/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch new file mode 100644 index 0000000000..5ff48e8b0d --- /dev/null +++ b/queue-6.18/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch @@ -0,0 +1,104 @@ +From c3947f09c16e91d36ea9b80a56fffe14b3671e07 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:56 +0100 +Subject: netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages() + +From: David Howells + +[ Upstream commit dbe556972100fabb8e5a1b3d2163831ff07b1e8e ] + +netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it +is wanting to unlock and compares that to rreq->no_unlock_folio so that it +doesn't unlock a folio being read for netfs_perform_write() or +netfs_write_begin(). + +However, given that netfs_unlock_abandoned_read_pages() is called _after_ +NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to +dereference is the one specified by ->no_unlock_folio as ownership +immediately reverts to the caller. + +Fix this by storing the folio pointer instead and using that rather than +the index. Also fix netfs_unlock_read_folio() where the same applies. + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com +cc: Paulo Alcantara +cc: Viacheslav Dubeyko +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 4 ++-- + fs/netfs/read_collect.c | 2 +- + fs/netfs/read_retry.c | 2 +- + include/linux/netfs.h | 2 +- + 4 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 085cd392bf5ac..ebaabec376326 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -669,7 +669,7 @@ int netfs_write_begin(struct netfs_inode *ctx, + ret = PTR_ERR(rreq); + goto error; + } +- rreq->no_unlock_folio = folio->index; ++ rreq->no_unlock_folio = folio; + __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); + + ret = netfs_begin_cache_read(rreq, ctx); +@@ -735,7 +735,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, + goto error; + } + +- rreq->no_unlock_folio = folio->index; ++ rreq->no_unlock_folio = folio; + __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); + ret = netfs_begin_cache_read(rreq, ctx); + if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) +diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c +index d2d902f466271..4c7312a4c8597 100644 +--- a/fs/netfs/read_collect.c ++++ b/fs/netfs/read_collect.c +@@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, + } + + just_unlock: +- if (folio->index == rreq->no_unlock_folio && ++ if (folio == rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { + _debug("no unlock"); + } else { +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index 68fc869513ef1..999177426141a 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -288,7 +288,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) + struct folio *folio = folioq_folio(p, slot); + + if (folio && !folioq_is_marked2(p, slot)) { +- if (folio->index == rreq->no_unlock_folio && ++ if (folio == rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, + &rreq->flags)) { + _debug("no unlock"); +diff --git a/include/linux/netfs.h b/include/linux/netfs.h +index ba17ac5bf356a..62a528f90666b 100644 +--- a/include/linux/netfs.h ++++ b/include/linux/netfs.h +@@ -252,7 +252,7 @@ struct netfs_io_request { + unsigned long long collected_to; /* Point we've collected to */ + unsigned long long cleaned_to; /* Position we've cleaned folios to */ + unsigned long long abandon_to; /* Position to abandon folios to */ +- pgoff_t no_unlock_folio; /* Don't unlock this folio after read */ ++ const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ + unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ + unsigned int debug_id; + unsigned int rsize; /* Maximum read size (0 for none) */ +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch b/queue-6.18/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch new file mode 100644 index 0000000000..34b05d7de4 --- /dev/null +++ b/queue-6.18/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch @@ -0,0 +1,93 @@ +From 4742d1a1557c8c76d915b24501cdcc15d80e2ca9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:52 +0100 +Subject: netfs: Fix read-gaps to remove netfs_folio from filled folio + +From: David Howells + +[ Upstream commit a41168aef634356a9b87ec44349e3c82835700a5 ] + +Fix netfs_read_gaps() to remove the netfs_folio record from the folio +record before marking the folio uptodate if it successfully fills the gaps +around the dirty data in a streaming write folio (dirty, but not uptodate). + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0x138b1 0x8b15d * + write 0x507ee 0x10df7 0x927c0 + write 0x19993 0x10e04 0x927c0 * + mapwrite 0x66214 0x1a253 0x927c0 + copy_range 0xb704 0x89b9 0x24429 0x79380 + write 0x2402b 0x144a2 0x90660 * + mapwrite 0x204d5 0x140a0 0x927c0 * + copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * + read 0 0x9157c 0x9157c + +on cifs with the default cache option. + +It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in +netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-16-dhowells@redhat.com +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index e6157122da3a4..3531c19eea97a 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -394,6 +394,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + { + struct netfs_io_request *rreq; + struct address_space *mapping = folio->mapping; ++ struct netfs_group *group = netfs_folio_group(folio); + struct netfs_folio *finfo = netfs_folio_info(folio); + struct netfs_inode *ctx = netfs_inode(mapping->host); + struct folio *sink = NULL; +@@ -460,6 +461,12 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + + ret = netfs_wait_for_read(rreq); + if (ret >= 0) { ++ if (group) ++ folio_change_private(folio, group); ++ else ++ folio_detach_private(folio); ++ kfree(finfo); ++ trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); + flush_dcache_folio(folio); + folio_mark_uptodate(folio); + } +@@ -495,10 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) + struct netfs_inode *ctx = netfs_inode(mapping->host); + int ret; + +- if (folio_test_dirty(folio)) { +- trace_netfs_folio(folio, netfs_folio_trace_read_gaps); ++ if (folio_test_dirty(folio)) + return netfs_read_gaps(file, folio); +- } + + _enter("%lx", folio->index); + +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-streaming-write-being-overwritten.patch b/queue-6.18/netfs-fix-streaming-write-being-overwritten.patch new file mode 100644 index 0000000000..6224afc126 --- /dev/null +++ b/queue-6.18/netfs-fix-streaming-write-being-overwritten.patch @@ -0,0 +1,176 @@ +From 9af6262fae71fa8482e1f2eb62c83433ea9ef7ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:50 +0100 +Subject: netfs: Fix streaming write being overwritten + +From: David Howells + +[ Upstream commit 7b4dcf1b9455a6e52ac7478b4057dbe10359576d ] + +In order to avoid reading whilst writing, netfslib will allow "streaming +writes" in which dirty data is stored directly into folios without reading +them first. Such folios are marked dirty but may not be marked uptodate. +If a folio is entirely written by a streaming write, uptodate will be set, +otherwise it will have a netfs_folio struct attached to ->private recording +the dirty region. + +In the event that a partially written streaming write page is to be +overwritten entirely by a single write(), netfs_perform_write() will try to +copy over it, but doesn't discard the netfs_folio if it succeeds; further, +it doesn't correctly handle a partial copy that overwrites some of the +dirty data. + +Fix this by the following: + + (1) If the folio is successfully overwritten, free the netfs_folio struct + before marking the page uptodate. + + (2) If the copy to the folio partially fails, but short of the dirty data, + just ignore the copy. + + (3) If the copy partially fails and overwrites some of the dirty data, + accept the copy, update the netfs_folio struct to record the new data. + If the folio is now filled, free the netfs_folio and set uptodate, + otherwise return a partial write. + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0 0x927c0 + write 0x63fb8 0x53c8 0 + copy_range 0xb704 0x19b9 0x24429 0x79380 + write 0x2402b 0x144a2 0x90660 * + write 0x204d5 0x140a0 0x927c0 * + copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * + read 0x00000 0x20000 0x9157c + read 0x20000 0x20000 0x9157c + read 0x40000 0x20000 0x9157c + read 0x60000 0x20000 0x9157c + read 0x7e1a0 0xcfb9 0x9157c + +on cifs with the default cache option. + +It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in +netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- + include/trace/events/netfs.h | 3 +++ + 2 files changed, 37 insertions(+), 13 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 1fa13c2629a73..99736895e964f 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -247,18 +247,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); +- if (unlikely(copied == 0)) ++ if (likely(copied == part)) { ++ if (finfo) { ++ trace = netfs_whole_folio_modify_filled; ++ goto folio_now_filled; ++ } ++ __netfs_set_group(folio, netfs_group); ++ folio_mark_uptodate(folio); ++ trace = netfs_whole_folio_modify; ++ goto copied; ++ } ++ if (copied == 0) + goto copy_failed; +- if (unlikely(copied < part)) { ++ if (!finfo || copied <= finfo->dirty_offset) { + maybe_trouble = true; + iov_iter_revert(iter, copied); + copied = 0; + folio_unlock(folio); + goto retry; + } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; ++ ++ /* We overwrote some existing dirty data, so we have to ++ * accept the partial write. ++ */ ++ finfo->dirty_len += finfo->dirty_offset; ++ if (finfo->dirty_len == flen) { ++ trace = netfs_whole_folio_modify_filled_efault; ++ goto folio_now_filled; ++ } ++ if (copied > finfo->dirty_len) ++ finfo->dirty_len = copied; ++ finfo->dirty_offset = 0; ++ trace = netfs_whole_folio_modify_efault; + goto copied; + } + +@@ -328,16 +348,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto copy_failed; + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); +- folio_mark_uptodate(folio); +- kfree(finfo); + trace = netfs_streaming_cont_filled_page; +- } else { +- trace = netfs_streaming_write_cont; ++ goto folio_now_filled; + } ++ trace = netfs_streaming_write_cont; + goto copied; + } + +@@ -351,6 +365,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + ++ folio_now_filled: ++ if (finfo->netfs_group) ++ folio_change_private(folio, finfo->netfs_group); ++ else ++ folio_detach_private(folio); ++ folio_mark_uptodate(folio); ++ kfree(finfo); + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index 88d814ba1e697..db045135406c9 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -177,6 +177,9 @@ + EM(netfs_folio_is_uptodate, "mod-uptodate") \ + EM(netfs_just_prefetch, "mod-prefetch") \ + EM(netfs_whole_folio_modify, "mod-whole-f") \ ++ EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ ++ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ ++ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch b/queue-6.18/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch new file mode 100644 index 0000000000..cdaf8eb68d --- /dev/null +++ b/queue-6.18/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch @@ -0,0 +1,169 @@ +From 9e83e3eab718f4d2e7e5df80fb3df8777980b28e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:44 +0100 +Subject: netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call + +From: Viacheslav Dubeyko + +[ Upstream commit dc7832d05deb4d632e8035e3299e31a3528fa0d0 ] + +The multiple runs of generic/013 test-case is capable +to reproduce a kernel BUG at mm/filemap.c:1504 with +probability of 30%. + +while true; do + sudo ./check generic/013 +done + +[ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 +[ 9849.452412] memcg:ffff8881a1915800 +[ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" +[ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) +[ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 +[ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 +[ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) +[ 9849.452474] ------------[ cut here ]------------ +[ 9849.452476] kernel BUG at mm/filemap.c:1504! +[ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI +[ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) +[ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 +0/2025 +[ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc +cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 +[ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 +[ 9849.504459] PKRU: 55555554 +[ 9849.504626] Call Trace: +[ 9849.505242] +[ 9849.505379] netfs_write_begin+0x7c8/0x10a0 +[ 9849.505877] ? __kasan_check_read+0x11/0x20 +[ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 +[ 9849.507178] ceph_write_begin+0x8c/0x1c0 +[ 9849.507934] generic_perform_write+0x391/0x8f0 +[ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 +[ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 +[ 9849.509581] ? ceph_get_caps+0x63/0xf0 +[ 9849.510259] ? ceph_get_caps+0x63/0xf0 +[ 9849.510530] ceph_write_iter+0xe79/0x1ae0 +[ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 +[ 9849.511839] ? lock_acquire+0x1ad/0x310 +[ 9849.512334] ? ksys_write+0xf9/0x230 +[ 9849.512582] ? lock_is_held_type+0xaa/0x140 +[ 9849.513128] vfs_write+0x512/0x1110 +[ 9849.513634] ? __fget_files+0x33/0x350 +[ 9849.513893] ? __pfx_vfs_write+0x10/0x10 +[ 9849.514143] ? mutex_lock_nested+0x1b/0x30 +[ 9849.514394] ksys_write+0xf9/0x230 +[ 9849.514621] ? __pfx_ksys_write+0x10/0x10 +[ 9849.514887] ? do_syscall_64+0x25e/0x1520 +[ 9849.515122] ? __kasan_check_read+0x11/0x20 +[ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.515655] __x64_sys_write+0x72/0xd0 +[ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 +[ 9849.516130] x64_sys_call+0x22f/0x2390 +[ 9849.516341] do_syscall_64+0x12b/0x1520 +[ 9849.516545] ? do_syscall_64+0x27c/0x1520 +[ 9849.516783] ? do_syscall_64+0x27c/0x1520 +[ 9849.517003] ? lock_release+0x318/0x480 +[ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 +[ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 +[ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.518073] ? do_syscall_64+0x25e/0x1520 +[ 9849.518291] ? __kasan_check_read+0x11/0x20 +[ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.518799] ? do_syscall_64+0x27c/0x1520 +[ 9849.519024] ? local_clock_noinstr+0xf/0x120 +[ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.519544] ? do_syscall_64+0x25e/0x1520 +[ 9849.519781] ? __kasan_check_read+0x11/0x20 +[ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520273] ? do_syscall_64+0x27c/0x1520 +[ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520767] ? irqentry_exit+0x10c/0x6c0 +[ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 +[ 9849.521224] ? exc_page_fault+0xab/0x130 +[ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.521766] RIP: 0033:0x7e36cbd14907 +[ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 +[ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 +[ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 +[ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 +[ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 +[ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 +[ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 +[ 9849.525447] +[ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore +[ 9849.529150] ---[ end trace 0000000000000000 ]--- +[ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 +[ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 +[ 9849.548679] PKRU: 55555554 + +The race sequence: +1. Read completes -> netfs_read_collection() runs +2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...) +3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin() +4. The netfs_unlock_abandoned_read_pages() unlocks the folio +5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO() + +The key reason of the issue that netfs_unlock_abandoned_read_pages() +doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes +folio_unlock() unconditionally. This patch implements in +netfs_unlock_abandoned_read_pages() logic similar to +netfs_unlock_read_folio(). + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Signed-off-by: Viacheslav Dubeyko +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +cc: Ceph Development +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/read_retry.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index cca9ac43c0773..68fc869513ef1 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -288,8 +288,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) + struct folio *folio = folioq_folio(p, slot); + + if (folio && !folioq_is_marked2(p, slot)) { +- trace_netfs_folio(folio, netfs_folio_trace_abandon); +- folio_unlock(folio); ++ if (folio->index == rreq->no_unlock_folio && ++ test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, ++ &rreq->flags)) { ++ _debug("no unlock"); ++ } else { ++ trace_netfs_folio(folio, ++ netfs_folio_trace_abandon); ++ folio_unlock(folio); ++ } + } + } + } +-- +2.53.0 + diff --git a/queue-6.18/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch b/queue-6.18/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch new file mode 100644 index 0000000000..ee6127b6f5 --- /dev/null +++ b/queue-6.18/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch @@ -0,0 +1,80 @@ +From dd1b43cec5f2bf65d7aecdd32853455857c31847 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:53 +0100 +Subject: netfs: Fix write streaming disablement if fd open O_RDWR + +From: David Howells + +[ Upstream commit 70a7b9193bbbfceaab5974de66834c64ccc875dd ] + +In netfs_perform_write(), "write streaming" (the caching of dirty data in +dirty but !uptodate folios) is performed to avoid the need to read data +that is just going to get immediately overwritten. However, this is/will +be disabled in three circumstances: if the fd is open O_RDWR, if fscache is +in use (as we need to round out the blocks for DIO) or if content +encryption is enabled (again for rounding out purposes). + +The idea behind disabling it if the fd is open O_RDWR is that we'd need to +flush the write-streaming page before we could read the data, particularly +through mmap. But netfs now fills in the gaps if ->read_folio() is called +on the page, so that is unnecessary. Further, this doesn't actually work +if a separate fd is open for reading. + +Fix this by removing the check for O_RDWR, thereby allowing streaming +writes even when we might read. + +This caused a number of problems with the generic/522 xfstest, but those +are now fixed. + +Fixes: c38f4e96e605 ("netfs: Provide func to copy data to pagecache for buffered write") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 99736895e964f..60215a7723574 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -204,11 +204,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + /* Decide how we should modify a folio. We might be attempting +- * to do write-streaming, in which case we don't want to a +- * local RMW cycle if we can avoid it. If we're doing local +- * caching or content crypto, we award that priority over +- * avoiding RMW. If the file is open readably, then we also +- * assume that we may want to read what we wrote. ++ * to do write-streaming, as we don't want to a local RMW cycle ++ * if we can avoid it. If we're doing local caching or content ++ * crypto, we award that priority over avoiding RMW. If the ++ * file is open readably, then we let ->read_folio() fill in ++ * the gaps. + */ + finfo = netfs_folio_info(folio); + group = netfs_folio_group(folio); +@@ -284,12 +284,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + + /* We don't want to do a streaming write on a file that loses + * caching service temporarily because the backing store got +- * culled and we don't really want to get a streaming write on +- * a file that's open for reading as ->read_folio() then has to +- * be able to flush it. ++ * culled. + */ +- if ((file->f_mode & FMODE_READ) || +- netfs_is_cache_enabled(ctx)) { ++ if (netfs_is_cache_enabled(ctx)) { + if (finfo) { + netfs_stat(&netfs_n_wh_wstream_conflict); + goto flush_content; +-- +2.53.0 + diff --git a/queue-6.18/nfsd-fix-infinite-loop-in-layout-state-revocation.patch b/queue-6.18/nfsd-fix-infinite-loop-in-layout-state-revocation.patch new file mode 100644 index 0000000000..9cc8b9bb0c --- /dev/null +++ b/queue-6.18/nfsd-fix-infinite-loop-in-layout-state-revocation.patch @@ -0,0 +1,46 @@ +From 345f0cf335af6d0c1471d9f5f0e7461090a53a6c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Apr 2026 14:52:59 -0400 +Subject: NFSD: Fix infinite loop in layout state revocation + +From: Chuck Lever + +[ Upstream commit 4f8ef58c10bfe5f86a643c7c8331b37e69e3dae1 ] + +find_one_sb_stid() skips stids whose sc_status is non-zero, but the +SC_TYPE_LAYOUT case in nfsd4_revoke_states() never sets sc_status +before calling nfsd4_close_layout(). The retry loop therefore finds +the same layout stid on every iteration, hanging the revoker +indefinitely. + +Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") +Reported-by: Dai Ngo +Reviewed-by: Jeff Layton +Tested-by: Dai Ngo +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index cb8096e94f518..a9e95df2fdb68 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -1848,6 +1848,13 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) + break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(stid); ++ spin_lock(&clp->cl_lock); ++ if (stid->sc_status == 0) { ++ stid->sc_status |= ++ SC_STATUS_ADMIN_REVOKED; ++ atomic_inc(&clp->cl_admin_revoked); ++ } ++ spin_unlock(&clp->cl_lock); + nfsd4_close_layout(ls); + break; + } +-- +2.53.0 + diff --git a/queue-6.18/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch b/queue-6.18/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch new file mode 100644 index 0000000000..3e61ab461c --- /dev/null +++ b/queue-6.18/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch @@ -0,0 +1,43 @@ +From 8d380f1c25710eb0db4a029196cf8312a27a2e23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 19:23:01 +0800 +Subject: nsfs: fix wrong error code returned for pidns ioctls + +From: Zhihao Cheng + +[ Upstream commit 725ecd80688bf3c57ca9205431f2c06174ff0756 ] + +When executing NS_GET_PID_FROM_PIDNS (or similar pidns ioctls), if the +target task cannot be found in the corresponding pid_ns, the error code +should be ESRCH instead of ENOTTY. + +This bug was introduced when the extensible ioctl handling was added. +Without proper return, ret would be overwritten by the default case in +the extensible ioctl switch statement. + +Fixes: a1d220d9dafa8 ("nsfs: iterate through mount namespaces") +Signed-off-by: Zhihao Cheng +Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com +Reviewed-by: Yang Erkun +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/nsfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/nsfs.c b/fs/nsfs.c +index f22c2a636e8f3..e2f9a725883c7 100644 +--- a/fs/nsfs.c ++++ b/fs/nsfs.c +@@ -261,7 +261,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl, + else + tsk = find_task_by_pid_ns(arg, pid_ns); + if (!tsk) +- break; ++ return ret; + + switch (ioctl) { + case NS_GET_PID_FROM_PIDNS: +-- +2.53.0 + diff --git a/queue-6.18/nvme-fix-bio-leak-on-mapping-failure.patch b/queue-6.18/nvme-fix-bio-leak-on-mapping-failure.patch new file mode 100644 index 0000000000..c4b36503d8 --- /dev/null +++ b/queue-6.18/nvme-fix-bio-leak-on-mapping-failure.patch @@ -0,0 +1,48 @@ +From 1a9ce3257d91be54f4fde5ae3b0017925cb0d48f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 06:16:02 -0700 +Subject: nvme: fix bio leak on mapping failure + +From: Keith Busch + +[ Upstream commit 2279cd9c61a330e5de4d6eb0bc422820dd6fdf36 ] + +The local bio is always NULL, so we'd leak the bio if the integrity +mapping failed. Just get it directly from the request. + +Fixes: d0d1d522316e91f ("blk-map: provide the bdev to bio if one exists") +Reviewed-by: Sagi Grimberg +Reviewed-by: John Garry +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/ioctl.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c +index c212fa952c0f4..5bbaf257fd6c5 100644 +--- a/drivers/nvme/host/ioctl.c ++++ b/drivers/nvme/host/ioctl.c +@@ -122,7 +122,6 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); + struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; + bool has_metadata = meta_buffer && meta_len; +- struct bio *bio = NULL; + int ret; + + if (!nvme_ctrl_sgl_supported(ctrl)) +@@ -154,8 +153,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + return ret; + + out_unmap: +- if (bio) +- blk_rq_unmap_user(bio); ++ if (req->bio) ++ blk_rq_unmap_user(req->bio); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch b/queue-6.18/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch new file mode 100644 index 0000000000..c0bf7251ef --- /dev/null +++ b/queue-6.18/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch @@ -0,0 +1,90 @@ +From bab9eed00eecb33fe59ad99703620f890e9c0434 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 16:11:16 +0800 +Subject: nvme-pci: fix use-after-free in nvme_free_host_mem() + +From: Chia-Lin Kao (AceLan) + +[ Upstream commit b35a13036755c5803168a7cb93bc66035c3e65b8 ] + +nvme_free_host_mem() frees dev->hmb_sgt via dma_free_noncontiguous() +but never clears the pointer afterward. This leads to a use-after-free +if nvme_free_host_mem() is called twice in the same error path. + +This can happen during nvme_probe() when nvme_setup_host_mem() succeeds +in allocating the HMB (setting dev->hmb_sgt) but nvme_set_host_mem() +fails with an I/O error: + + nvme_setup_host_mem() + nvme_alloc_host_mem_single() -> sets dev->hmb_sgt + nvme_set_host_mem() -> fails with -EIO + nvme_free_host_mem() -> frees hmb_sgt, but does NOT NULL it + return error + + nvme_probe() error path: + nvme_free_host_mem() -> dev->hmb_sgt is stale, use-after-free + +The second call dereferences the freed sgt, causing a NULL pointer +dereference in iommu_dma_free_noncontiguous() when it accesses +sgt->sgl->dma_address (the backing memory has been freed and zeroed). + +This is reproducible on Thunderbolt-attached NVMe devices (e.g., OWC +Envoy Express behind a Dell WD22TB4 dock) where the device intermittently +returns I/O errors during HMB setup due to PCIe link instability. + + BUG: kernel NULL pointer dereference, address: 0000000000000010 + RIP: 0010:iommu_dma_free_noncontiguous+0x22/0x80 + Call Trace: + + dma_free_noncontiguous+0x3b/0x130 + nvme_free_host_mem+0x30/0xf0 [nvme] + nvme_probe.cold+0xcc/0x275 [nvme] + local_pci_probe+0x43/0xa0 + pci_device_probe+0xeea/0x290 + really_probe+0xf9/0x3b0 + __driver_probe_device+0x8b/0x170 + driver_probe_device+0x24/0xd0 + __driver_attach_async_helper+0x6b/0x110 + async_run_entry_fn+0x37/0x170 + process_one_work+0x1ac/0x3d0 + worker_thread+0x1b8/0x360 + kthread+0xf7/0x130 + ret_from_fork+0x2d8/0x3a0 + ret_from_fork_asm+0x1a/0x30 + + +Fix this by setting dev->hmb_sgt to NULL after freeing it, so the +second call takes the multi-descriptor path which safely handles the +already-cleaned-up state. + +Fixes: 63a5c7a4b4c4 ("nvme-pci: use dma_alloc_noncontigous if possible") +Signed-off-by: Chia-Lin Kao (AceLan) +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 2e32242bed67c..5e36a5926fe03 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -2320,11 +2320,13 @@ static void nvme_free_host_mem_multi(struct nvme_dev *dev) + + static void nvme_free_host_mem(struct nvme_dev *dev) + { +- if (dev->hmb_sgt) ++ if (dev->hmb_sgt) { + dma_free_noncontiguous(dev->dev, dev->host_mem_size, + dev->hmb_sgt, DMA_BIDIRECTIONAL); +- else ++ dev->hmb_sgt = NULL; ++ } else { + nvme_free_host_mem_multi(dev); ++ } + + dma_free_coherent(dev->dev, dev->host_mem_descs_size, + dev->host_mem_descs, dev->host_mem_descs_dma); +-- +2.53.0 + diff --git a/queue-6.18/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch b/queue-6.18/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch new file mode 100644 index 0000000000..bace4f5365 --- /dev/null +++ b/queue-6.18/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch @@ -0,0 +1,42 @@ +From 3eb7ad2acdf89e8f6c0615cee3776ac63e951264 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 10:00:36 +0530 +Subject: octeontx2-af: npc: Fix allmulticast skip logic for LBK and SDP VFs + +From: Ratheesh Kannoth + +[ Upstream commit 9eddc819f00b5b74bb4ac91396f80bd35f5f3561 ] + +When installing the allmulticast NPC rule, rvu_npc_install_allmulti_entry() +should skip LBK and SDP VFs (only CGX PF/VF may add the entry). The +code combined is_lbk_vf() and is_sdp_vf() with logical AND, which is +never true for a single pcifunc, so the intended early return never ran. + +Use logical OR instead. + +Cc: Geetha sowjanya +Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks") +Signed-off-by: Ratheesh Kannoth +Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index 8658cb2143dfc..e28675fe18907 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -837,7 +837,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, + u16 vf_func; + + /* Only CGX PF/VF can add allmulticast entry */ +- if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc)) ++ if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc)) + return; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); +-- +2.53.0 + diff --git a/queue-6.18/ovpn-disable-bhs-when-updating-device-stats.patch b/queue-6.18/ovpn-disable-bhs-when-updating-device-stats.patch new file mode 100644 index 0000000000..d417e02d4f --- /dev/null +++ b/queue-6.18/ovpn-disable-bhs-when-updating-device-stats.patch @@ -0,0 +1,183 @@ +From 1023f5d8ae8999ec1048357887707f30f9bfafeb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 15:26:10 +0200 +Subject: ovpn: disable BHs when updating device stats + +From: Ralf Lici + +[ Upstream commit 0c0dddc07d272a8d25922e48041e8e4d2434df7e ] + +ovpn updates dev->dstats from both process and softirq contexts. In +particular, TCP paths may run from socket callbacks, workqueues or +strparser work, while UDP receive and ovpn's ndo_start_xmit path may +update the same per-device dstats from BH context. + +Add ovpn device drop-stat helpers that disable BHs around +dev_dstats_rx_dropped() and dev_dstats_tx_dropped(), and use them for +drop accounting. + +The successful RX dev_dstats_rx_add() update is already covered by the +BH-disabled section around gro_cells_receive(). For the successful TCP +TX dev_dstats_tx_add() update, replace the existing preempt-disabled +section with a BH-disabled one. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Signed-off-by: Ralf Lici +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/io.c | 12 ++++++------ + drivers/net/ovpn/stats.h | 16 ++++++++++++++++ + drivers/net/ovpn/tcp.c | 10 +++++----- + drivers/net/ovpn/udp.c | 2 +- + 4 files changed, 28 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c +index 955c9a37e1f8d..c03e58e28a860 100644 +--- a/drivers/net/ovpn/io.c ++++ b/drivers/net/ovpn/io.c +@@ -196,7 +196,7 @@ void ovpn_decrypt_post(void *data, int ret) + skb = NULL; + drop: + if (unlikely(skb)) +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + kfree_skb(skb); + drop_nocount: + if (likely(peer)) +@@ -220,7 +220,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) + net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n", + netdev_name(peer->ovpn->dev), peer->id, + key_id); +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + kfree_skb(skb); + ovpn_peer_put(peer); + return; +@@ -298,7 +298,7 @@ void ovpn_encrypt_post(void *data, int ret) + rcu_read_unlock(); + err: + if (unlikely(skb)) +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + if (likely(peer)) + ovpn_peer_put(peer); + if (likely(ks)) +@@ -340,7 +340,7 @@ static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb, + */ + skb_list_walk_safe(skb, curr, next) { + if (unlikely(!ovpn_encrypt_one(peer, curr))) { +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + kfree_skb(curr); + } + } +@@ -411,7 +411,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) + if (unlikely(!curr)) { + net_err_ratelimited("%s: skb_share_check failed for payload packet\n", + netdev_name(dev)); +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + continue; + } + +@@ -437,7 +437,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) + drop: + ovpn_peer_put(peer); + drop_no_peer: +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + skb_tx_error(skb); + kfree_skb_list(skb); + return NETDEV_TX_OK; +diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h +index 53433d8b6c331..3a45b97c00568 100644 +--- a/drivers/net/ovpn/stats.h ++++ b/drivers/net/ovpn/stats.h +@@ -11,6 +11,8 @@ + #ifndef _NET_OVPN_OVPNSTATS_H_ + #define _NET_OVPN_OVPNSTATS_H_ + ++#include ++ + /* one stat */ + struct ovpn_peer_stat { + atomic64_t bytes; +@@ -44,4 +46,18 @@ static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats, + ovpn_peer_stats_increment(&stats->tx, n); + } + ++static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev) ++{ ++ local_bh_disable(); ++ dev_dstats_tx_dropped(dev); ++ local_bh_enable(); ++} ++ ++static inline void ovpn_dev_dstats_rx_dropped(struct net_device *dev) ++{ ++ local_bh_disable(); ++ dev_dstats_rx_dropped(dev); ++ local_bh_enable(); ++} ++ + #endif /* _NET_OVPN_OVPNSTATS_H_ */ +diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c +index 5f345ae7d59d2..505c2f214c9f1 100644 +--- a/drivers/net/ovpn/tcp.c ++++ b/drivers/net/ovpn/tcp.c +@@ -152,7 +152,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) + if (WARN_ON(!ovpn_peer_hold(peer))) + goto err_nopeer; + schedule_work(&peer->tcp.defer_del_work); +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + err_nopeer: + kfree_skb(skb); + } +@@ -298,9 +298,9 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) + } while (peer->tcp.out_msg.len > 0); + + if (!peer->tcp.out_msg.len) { +- preempt_disable(); ++ local_bh_disable(); + dev_dstats_tx_add(peer->ovpn->dev, skb->len); +- preempt_enable(); ++ local_bh_enable(); + } + + kfree_skb(peer->tcp.out_msg.skb); +@@ -331,7 +331,7 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk, + ovpn_tcp_send_sock(peer, sk); + + if (peer->tcp.out_msg.skb) { +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + kfree_skb(skb); + return; + } +@@ -353,7 +353,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, + if (sock_owned_by_user(sk)) { + if (skb_queue_len(&peer->tcp.out_queue) >= + READ_ONCE(net_hotdata.max_backlog)) { +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + kfree_skb(skb); + goto unlock; + } +diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c +index 272b535ecaad4..367563d84472f 100644 +--- a/drivers/net/ovpn/udp.c ++++ b/drivers/net/ovpn/udp.c +@@ -126,7 +126,7 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) + return 0; + + drop: +- dev_dstats_rx_dropped(ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(ovpn->dev); + drop_noovpn: + kfree_skb(skb); + return 0; +-- +2.53.0 + diff --git a/queue-6.18/ovpn-fix-race-between-deleting-interface-and-adding-.patch b/queue-6.18/ovpn-fix-race-between-deleting-interface-and-adding-.patch new file mode 100644 index 0000000000..b5bae6ed43 --- /dev/null +++ b/queue-6.18/ovpn-fix-race-between-deleting-interface-and-adding-.patch @@ -0,0 +1,124 @@ +From 00d8cdbbfd6d9e5173ebe398f1211ecc1dfacbee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Mar 2026 14:47:56 +0100 +Subject: ovpn: fix race between deleting interface and adding new peer + +From: Antonio Quartulli + +[ Upstream commit 982422b11e6f95f766a8cd2c2b1cbdb77e234a61 ] + +While deleting an existing ovpn interface, there is a very +narrow window where adding a new peer via netlink may cause +the netdevice to hang and prevent its unregistration. + +It may happen during ovpn_dellink(), when all existing peers are +freed and the device is queued for deregistration, but a +CMD_PEER_NEW message comes in adding a new peer that takes again +a reference to the netdev. + +At this point there is no way to release the device because we are +under the assumption that all peers were already released. + +Fix the race condition by releasing all peers in ndo_uninit(), +when the netdevice has already been removed from the netdev +list. + +Also ovpn_peer_add() has now an extra check that forces the +function to bail out if the device reg_state is not REGISTERED. +This way any incoming CMD_PEER_NEW racing with the interface +deletion routine will simply stop before adding the peer. + +Note that the above check happens while holding the netdev_lock +to prevent racing netdev state changes. + +ovpn_dellink() is now empty and can be removed. + +Reported-by: Hyunwoo Kim +Closes: https://lore.kernel.org/netdev/aaVgJ16edTfQkYbx@v4bel/ +Suggested-by: Sabrina Dubroca +Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object") +Reviewed-by: Sabrina Dubroca +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/main.c | 12 ++---------- + drivers/net/ovpn/peer.c | 21 ++++++++++++++++++--- + 2 files changed, 20 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c +index 1bb1afe766a41..3f76b1b0e5f60 100644 +--- a/drivers/net/ovpn/main.c ++++ b/drivers/net/ovpn/main.c +@@ -92,6 +92,8 @@ static void ovpn_net_uninit(struct net_device *dev) + { + struct ovpn_priv *ovpn = netdev_priv(dev); + ++ disable_delayed_work_sync(&ovpn->keepalive_work); ++ ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); + gro_cells_destroy(&ovpn->gro_cells); + } + +@@ -208,15 +210,6 @@ static int ovpn_newlink(struct net_device *dev, + return register_netdevice(dev); + } + +-static void ovpn_dellink(struct net_device *dev, struct list_head *head) +-{ +- struct ovpn_priv *ovpn = netdev_priv(dev); +- +- cancel_delayed_work_sync(&ovpn->keepalive_work); +- ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); +- unregister_netdevice_queue(dev, head); +-} +- + static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev) + { + struct ovpn_priv *ovpn = netdev_priv(dev); +@@ -235,7 +228,6 @@ static struct rtnl_link_ops ovpn_link_ops = { + .policy = ovpn_policy, + .maxtype = IFLA_OVPN_MAX, + .newlink = ovpn_newlink, +- .dellink = ovpn_dellink, + .fill_info = ovpn_fill_info, + }; + +diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c +index a3d856724b84a..87a83321f1dd5 100644 +--- a/drivers/net/ovpn/peer.c ++++ b/drivers/net/ovpn/peer.c +@@ -1029,14 +1029,29 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer) + */ + int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer) + { ++ int ret = -ENODEV; ++ ++ /* Prevent adding new peers while destroying the ovpn interface. ++ * Failing to do so would end up holding the device reference ++ * endlessly hostage of the new peer object with no chance of ++ * release.. ++ */ ++ netdev_lock(ovpn->dev); ++ if (ovpn->dev->reg_state != NETREG_REGISTERED) ++ goto out; ++ + switch (ovpn->mode) { + case OVPN_MODE_MP: +- return ovpn_peer_add_mp(ovpn, peer); ++ ret = ovpn_peer_add_mp(ovpn, peer); ++ break; + case OVPN_MODE_P2P: +- return ovpn_peer_add_p2p(ovpn, peer); ++ ret = ovpn_peer_add_p2p(ovpn, peer); ++ break; + } ++out: ++ netdev_unlock(ovpn->dev); + +- return -EOPNOTSUPP; ++ return ret; + } + + /** +-- +2.53.0 + diff --git a/queue-6.18/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch b/queue-6.18/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch new file mode 100644 index 0000000000..2e295ad4a9 --- /dev/null +++ b/queue-6.18/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch @@ -0,0 +1,108 @@ +From d959978e063a7f8726caa75ed31bf641582aca2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 11:55:21 +0100 +Subject: ovpn: respect peer refcount in CMD_NEW_PEER error path + +From: David Carlier + +[ Upstream commit 1fef6614673ff0846d30acdeeaf3cf98bb5f6116 ] + +ovpn_nl_peer_new_doit()'s error path calls ovpn_peer_release() directly +rather than ovpn_peer_put(), bypassing the kref. The accompanying +comment ("peer was not yet hashed, thus it is not used in any context") +holds for UDP but not for TCP. + +For UDP, the ovpn_socket union uses the .ovpn arm and never points back +at a peer; UDP encap_recv looks up peers via the not-yet-populated +hashtables, so the new peer is unreachable until ovpn_peer_add() +publishes it. + +For TCP, ovpn_socket_new() sets ovpn_sock->peer and +ovpn_tcp_socket_attach() publishes ovpn_sock via rcu_assign_sk_user_data(). +From that moment until ovpn_socket_release() detaches in the error path, +the TCP fd is fully wired: userspace recvmsg / sendmsg / close / poll +on the fd, as well as the strparser-driven ovpn_tcp_rcv() path, can +reach the peer through sk_user_data -> ovpn_sock->peer and bump its +refcount via ovpn_peer_hold(). + +ovpn_tcp_socket_wait_finish() (called inside ovpn_socket_release()) +drains strparser and the tx work, but does not synchronize with +userspace syscall callers that already hold a peer reference. If +ovpn_nl_peer_modify() or ovpn_peer_add() returns an error while such +a caller is in flight - notably an ovpn_tcp_recvmsg() blocked in +__skb_recv_datagram() on peer->tcp.user_queue - the direct +ovpn_peer_release() destroys the peer while the caller still holds +the reference, and the eventual ovpn_peer_put() from that caller +operates on freed memory. + +Replace the direct destructor call with ovpn_peer_put() so the kref +correctly defers destruction until the last reference is dropped. +In the common case where no concurrent user is present, behaviour is +unchanged: the kref hits zero immediately and ovpn_peer_release_kref() +runs the same destructor. + +With this conversion ovpn_peer_release() has no callers outside peer.c +- ovpn_peer_release_kref() in the same translation unit is the only +remaining user - so make it static and drop its declaration from +peer.h. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Reviewed-by: Sabrina Dubroca +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: David Carlier +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/netlink.c | 8 +++++--- + drivers/net/ovpn/peer.c | 2 +- + drivers/net/ovpn/peer.h | 1 - + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c +index c7f3824376302..bdb56ef0c9040 100644 +--- a/drivers/net/ovpn/netlink.c ++++ b/drivers/net/ovpn/netlink.c +@@ -455,10 +455,12 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) + sock_release: + ovpn_socket_release(peer); + peer_release: +- /* release right away because peer was not yet hashed, thus it is not +- * used in any context ++ /* For UDP, the peer is unreachable until added to the hashtables, so ++ * dropping the initial reference is enough. For TCP, the peer may be ++ * concurrently reachable via sk_user_data->peer until ++ * ovpn_socket_release() detaches; rely on the refcount. + */ +- ovpn_peer_release(peer); ++ ovpn_peer_put(peer); + + return ret; + } +diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c +index 4bfcab0c8652e..a3d856724b84a 100644 +--- a/drivers/net/ovpn/peer.c ++++ b/drivers/net/ovpn/peer.c +@@ -348,7 +348,7 @@ static void ovpn_peer_release_rcu(struct rcu_head *head) + * ovpn_peer_release - release peer private members + * @peer: the peer to release + */ +-void ovpn_peer_release(struct ovpn_peer *peer) ++static void ovpn_peer_release(struct ovpn_peer *peer) + { + ovpn_crypto_state_release(&peer->crypto); + spin_lock_bh(&peer->lock); +diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h +index a1423f2b09e06..4de5aeae33f7d 100644 +--- a/drivers/net/ovpn/peer.h ++++ b/drivers/net/ovpn/peer.h +@@ -125,7 +125,6 @@ static inline bool ovpn_peer_hold(struct ovpn_peer *peer) + return kref_get_unless_zero(&peer->refcount); + } + +-void ovpn_peer_release(struct ovpn_peer *peer); + void ovpn_peer_release_kref(struct kref *kref); + + /** +-- +2.53.0 + diff --git a/queue-6.18/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch b/queue-6.18/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch new file mode 100644 index 0000000000..4fa588b7c2 --- /dev/null +++ b/queue-6.18/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch @@ -0,0 +1,84 @@ +From 3cde6cbeece31fc07563a31f4cfc9296504744fe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 11:55:20 +0100 +Subject: ovpn: tcp - use cached peer pointer in ovpn_tcp_close() + +From: David Carlier + +[ Upstream commit 775d8d7ad02aa345e1588424a6a8b9ae49fb9012 ] + +ovpn_tcp_close() loads the ovpn_socket via rcu_dereference_sk_user_data() +under rcu_read_lock(), takes a reference on sock->peer, caches the peer +pointer in a local, and drops the read lock. It then passes sock->peer +(rather than the cached local) to ovpn_peer_del(), re-dereferencing the +ovpn_socket after the RCU read section has ended. + +Unlike ovpn_tcp_sendmsg(), which uses the same "load under RCU, use +after unlock" pattern but is protected by lock_sock() held across the +function, ovpn_tcp_close() runs without the socket lock: inet_release() +invokes sk_prot->close() without taking lock_sock first. + +ovpn_socket_release() can therefore complete its kref_put -> detach -> +synchronize_rcu -> kfree(sock) sequence concurrently, in the window +after ovpn_tcp_close() drops rcu_read_lock() but before it dereferences +sock->peer. The synchronize_rcu() in ovpn_socket_release() protects +readers that use the dereferenced pointer inside the RCU read section, +not those that escape the pointer to a local and use it afterwards. + +A reproducer follows the pattern of commit 94560267d6c4 ("ovpn: tcp - +don't deref NULL sk_socket member after tcp_close()"): trigger a peer +removal (keepalive expiration or netlink OVPN_CMD_DEL_PEER) at the same +moment userspace closes the TCP fd. That commit fixed the detach-side +of the same race window; this one fixes the close-side at a different +victim. + +Tighten the entry block to read sock->peer exactly once into the cached +peer local, and route all subsequent uses (the hold check, the +ovpn_peer_del() call, and the prot->close() invocation) through that +local. sock->peer is only ever written once in ovpn_socket_new() under +lock_sock(), before rcu_assign_sk_user_data() publishes the ovpn_socket, +and is never reassigned afterwards - but the previous multi-read pattern +made that invariant implicit rather than explicit. The same multi-read +shape exists in ovpn_tcp_recvmsg(), ovpn_tcp_sendmsg(), +ovpn_tcp_data_ready() and ovpn_tcp_write_space(); those will be cleaned +up via a dedicated helper in a follow-up net-next series. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Reviewed-by: Sabrina Dubroca +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: David Carlier +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/tcp.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c +index 5499c1572f3e2..5f345ae7d59d2 100644 +--- a/drivers/net/ovpn/tcp.c ++++ b/drivers/net/ovpn/tcp.c +@@ -581,14 +581,19 @@ static void ovpn_tcp_close(struct sock *sk, long timeout) + + rcu_read_lock(); + sock = rcu_dereference_sk_user_data(sk); +- if (!sock || !sock->peer || !ovpn_peer_hold(sock->peer)) { ++ if (!sock) { + rcu_read_unlock(); + return; + } ++ + peer = sock->peer; ++ if (!peer || !ovpn_peer_hold(peer)) { ++ rcu_read_unlock(); ++ return; ++ } + rcu_read_unlock(); + +- ovpn_peer_del(sock->peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); ++ ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); + peer->tcp.sk_cb.prot->close(sk, timeout); + ovpn_peer_put(peer); + } +-- +2.53.0 + diff --git a/queue-6.18/pds_core-ensure-null-termination-for-firmware-versio.patch b/queue-6.18/pds_core-ensure-null-termination-for-firmware-versio.patch new file mode 100644 index 0000000000..761b91327a --- /dev/null +++ b/queue-6.18/pds_core-ensure-null-termination-for-firmware-versio.patch @@ -0,0 +1,47 @@ +From 2108a08b2b7e32b4f5ce55b44d356a89d2d8f29b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:58:42 +0000 +Subject: pds_core: ensure null-termination for firmware version strings + +From: Nikhil P. Rao + +[ Upstream commit 3d4432d34c1992701289cbe12df9fd024f315998 ] + +The driver passes fw_version directly to devlink_info_version_stored_put() +without ensuring null-termination. While current firmware null-terminates +these strings, the driver should not rely on this behavior. Add explicit +null-termination to prevent potential issues if firmware behavior changes. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c +index d8dc39da4161f..621791a3c543b 100644 +--- a/drivers/net/ethernet/amd/pds_core/devlink.c ++++ b/drivers/net/ethernet/amd/pds_core/devlink.c +@@ -121,12 +121,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + + listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); + for (i = 0; i < listlen; i++) { ++ char *fw_ver = fw_list.fw_names[i].fw_version; ++ + if (i < ARRAY_SIZE(fw_slotnames)) + strscpy(buf, fw_slotnames[i], sizeof(buf)); + else + snprintf(buf, sizeof(buf), "fw.slot_%d", i); +- err = devlink_info_version_stored_put(req, buf, +- fw_list.fw_names[i].fw_version); ++ fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0'; ++ err = devlink_info_version_stored_put(req, buf, fw_ver); + if (err) + return err; + } +-- +2.53.0 + diff --git a/queue-6.18/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch b/queue-6.18/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch new file mode 100644 index 0000000000..264b367390 --- /dev/null +++ b/queue-6.18/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch @@ -0,0 +1,50 @@ +From c00e52c151be87a3d827e226e7408d2f2e1d3e35 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:07 +0000 +Subject: pds_core: fix debugfs_lookup dentry leak and error handling + +From: Nikhil P. Rao + +[ Upstream commit dc416e32baaeb620b9809e9e25fc7b30889686e9 ] + +debugfs_lookup() returns a dentry with an elevated reference count that +must be released with dput(). The current code discards the returned +dentry without calling dput(), causing a reference leak on every +firmware reset recovery. + +Additionally, when CONFIG_DEBUG_FS is disabled, debugfs_lookup() +returns ERR_PTR(-ENODEV), not NULL. The current check passes for error +pointers and would call dput() on an invalid pointer, causing a crash. + +Fixes: bc90fbe0c318 ("pds_core: Rework teardown/setup flow to be more common") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/debugfs.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c +index 04c5e3abd8d70..810a0cd9bcac8 100644 +--- a/drivers/net/ethernet/amd/pds_core/debugfs.c ++++ b/drivers/net/ethernet/amd/pds_core/debugfs.c +@@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity); + + void pdsc_debugfs_add_ident(struct pdsc *pdsc) + { ++ struct dentry *dentry; ++ + /* This file will already exist in the reset flow */ +- if (debugfs_lookup("identity", pdsc->dentry)) ++ dentry = debugfs_lookup("identity", pdsc->dentry); ++ if (!IS_ERR_OR_NULL(dentry)) { ++ dput(dentry); + return; ++ } + + debugfs_create_file("identity", 0400, pdsc->dentry, + pdsc, &identity_fops); +-- +2.53.0 + diff --git a/queue-6.18/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch b/queue-6.18/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch new file mode 100644 index 0000000000..a8a1b0f187 --- /dev/null +++ b/queue-6.18/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch @@ -0,0 +1,62 @@ +From 935bfa7cc35f617715435acede9bc1d257039a2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:05 +0000 +Subject: pds_core: fix error handling in pdsc_devcmd_wait + +From: Nikhil P. Rao + +[ Upstream commit 0e46b6635b03d29807f810c3b415c4755a3f958d ] + +Fix two cases where pdsc_devcmd_wait() returns stale success from +the completion register instead of an error: + +1. FW crash: If firmware stops running, the wait loop breaks early with + running=false. The condition "if ((!done || timeout) && running)" is + false, so error handling is bypassed and stale status is returned. + Check !running first and return -ENXIO. + +2. Timeout: If a command times out, err is set to -ETIMEDOUT but then + overwritten by pdsc_err_to_errno(status) which reads stale status. + Return -ETIMEDOUT immediately after cleaning up. + +Both errors now propagate to pdsc_devcmd_locked() which queues +health_work for recovery. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/dev.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c +index 495ef4ef8c103..1d1e559bd99d3 100644 +--- a/drivers/net/ethernet/amd/pds_core/dev.c ++++ b/drivers/net/ethernet/amd/pds_core/dev.c +@@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds) + dev_dbg(dev, "DEVCMD %d %s after %ld secs\n", + opcode, pdsc_devcmd_str(opcode), duration / HZ); + +- if ((!done || timeout) && running) { ++ if (!running) { ++ dev_err(dev, "DEVCMD %d %s fw not running\n", ++ opcode, pdsc_devcmd_str(opcode)); ++ pdsc_devcmd_clean(pdsc); ++ return -ENXIO; ++ } ++ ++ if (!done || timeout) { + dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n", + opcode, pdsc_devcmd_str(opcode), done, timeout, + max_seconds); +- err = -ETIMEDOUT; + pdsc_devcmd_clean(pdsc); ++ return -ETIMEDOUT; + } + + status = pdsc_devcmd_status(pdsc); +-- +2.53.0 + diff --git a/queue-6.18/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-6.18/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..4cdc55f877 --- /dev/null +++ b/queue-6.18/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From a917c9d3938f91a50fbc1523cce9e70d9fe6fb2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 04f4fb4bed702..f882bc57649c7 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch b/queue-6.18/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch new file mode 100644 index 0000000000..48743024bc --- /dev/null +++ b/queue-6.18/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch @@ -0,0 +1,60 @@ +From 9cd32920537d34cb2c9bfbbef9b3740fe5864afd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Apr 2026 11:44:13 +0000 +Subject: pinctrl: meson: amlogic-a4: fix deadlock issue + +From: Xianwei Zhao + +[ Upstream commit e72ce029810390eb987a036fb2c8a5da9a23b685 ] + +Accessing the pinconf-pins sysfs node may deadlock. + +pinconf_pins_show() holds pctldev->mutex, and the platform driver +calls pinctrl_find_gpio_range_from_pin(), which tries to acquire +the same mutex again, leading to a deadlock. + +Use pinctrl_find_gpio_range_from_pin_nolock() to fix this issue. + +Fixes: 6e9be3abb78c ("pinctrl: Add driver support for Amlogic SoCs") +Signed-off-by: Xianwei Zhao +Reviewed-by: Neil Armstrong +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +index e2293a872dcb7..35d27626a336b 100644 +--- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c ++++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +@@ -292,7 +292,7 @@ static int aml_calc_reg_and_bit(struct pinctrl_gpio_range *range, + static int aml_pinconf_get_pull(struct aml_pinctrl *info, unsigned int pin) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit, val; + int ret, conf; +@@ -326,7 +326,7 @@ static int aml_pinconf_get_drive_strength(struct aml_pinctrl *info, + u16 *drive_strength_ua) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit; + unsigned int val; +@@ -365,7 +365,7 @@ static int aml_pinconf_get_gpio_bit(struct aml_pinctrl *info, + unsigned int reg_type) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit, val; + int ret; +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch b/queue-6.18/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch new file mode 100644 index 0000000000..76f06c6c0e --- /dev/null +++ b/queue-6.18/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch @@ -0,0 +1,45 @@ +From e86d24f7aae8a85ea7d18bd6fa23e36a6048316c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Apr 2026 16:55:24 +0530 +Subject: pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615 + +From: Maulik Shah + +[ Upstream commit 9d69033ad967b6e09b1e5b30d1a32c6c4876465d ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but qcs615 do not +support i3c. GPIOs 39,51,88 and 89 are also connected to different PDC +pin to support non-ibi wakeup. Update the wakeirq map to reflect same. + +Fixes: b698f36a9d40 ("pinctrl: qcom: add the tlmm driver for QCS615 platform") +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Reviewed-by: Konrad Dybcio +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-qcs615.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c +index f1c827ddbfbfa..4d474c312c10b 100644 +--- a/drivers/pinctrl/qcom/pinctrl-qcs615.c ++++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c +@@ -1043,11 +1043,11 @@ static const struct msm_pingroup qcs615_groups[] = { + static const struct msm_gpio_wakeirq_map qcs615_pdc_map[] = { + { 1, 45 }, { 3, 31 }, { 7, 55 }, { 9, 110 }, { 11, 34 }, + { 13, 33 }, { 14, 35 }, { 17, 46 }, { 19, 48 }, { 21, 83 }, +- { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 125 }, { 41, 47 }, +- { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 123 }, { 55, 56 }, ++ { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 118 }, { 41, 47 }, ++ { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 116 }, { 55, 56 }, + { 56, 57 }, { 57, 58 }, { 60, 60 }, { 71, 54 }, { 80, 73 }, + { 81, 64 }, { 82, 50 }, { 83, 65 }, { 84, 92 }, { 85, 99 }, +- { 86, 67 }, { 87, 84 }, { 88, 124 }, { 89, 122 }, { 90, 69 }, ++ { 86, 67 }, { 87, 84 }, { 88, 117 }, { 89, 115 }, { 90, 69 }, + { 92, 88 }, { 93, 75 }, { 94, 91 }, { 95, 72 }, { 96, 82 }, + { 97, 74 }, { 98, 95 }, { 99, 94 }, { 100, 100 }, { 101, 40 }, + { 102, 93 }, { 103, 77 }, { 104, 78 }, { 105, 96 }, { 107, 97 }, +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch b/queue-6.18/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch new file mode 100644 index 0000000000..55ab441b5c --- /dev/null +++ b/queue-6.18/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch @@ -0,0 +1,56 @@ +From e5baae712635b7cf51dddd5e679b653d0485478f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:44:58 +0530 +Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for + sm8150 + +From: Maulik Shah + +[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not +support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC +pin and already reflected in the wake irq map. + +Remove the unsupported wakeup interrupts from the map. + +Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") +Reviewed-by: Konrad Dybcio +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c +index ad861cd66958c..e4c561a9c50ae 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c ++++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c +@@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, + { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, +- { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, +- { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, ++ { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, ++ { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, + { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, + { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, +- { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, ++ { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, + { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, + { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, + { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, +- { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, ++ { 144, 115 }, { 147, 102 }, { 150, 107 }, + { 152, 108 }, { 153, 109 } + }; + +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch b/queue-6.18/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch new file mode 100644 index 0000000000..8d251ee41b --- /dev/null +++ b/queue-6.18/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch @@ -0,0 +1,65 @@ +From 07be86348c7b8b363fc1d17e43f463265391cdde Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Apr 2026 15:52:34 +0200 +Subject: pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function + +From: Til Kaiser + +[ Upstream commit b51d33ea8a164bb5f0eec8ad817fa9730ac2b577 ] + +The qcom pinctrl core supports marking functions that represent GPIO mode +via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject +GPIO requests for pins that are muxed to the GPIO function. + +ipq4019 still describes its gpio function with QCA_PIN_FUNCTION(gpio), +so it is not treated as a GPIO pin function. As a result, GPIO consumers +can still conflict with pinctrl states that select the "gpio" function. + +Add a QCA_GPIO_PIN_FUNCTION() helper and use it for the ipq4019 gpio +function, matching how the msm-based qcom drivers handle this. + +This allows ipq4019 to keep the GPIO-related pin configuration in DTS +without tripping over strict pinmux ownership checks. + +Fixes: cc85cb96e2e4 ("pinctrl: qcom: make the pinmuxing strict") +Signed-off-by: Til Kaiser +Reviewed-by: Dmitry Baryshkov +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-ipq4019.c | 2 +- + drivers/pinctrl/qcom/pinctrl-msm.h | 5 +++++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c +index 6ede3149b6e17..07df812fb7282 100644 +--- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c ++++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c +@@ -480,7 +480,7 @@ static const struct pinfunction ipq4019_functions[] = { + QCA_PIN_FUNCTION(blsp_uart0), + QCA_PIN_FUNCTION(blsp_uart1), + QCA_PIN_FUNCTION(chip_rst), +- QCA_PIN_FUNCTION(gpio), ++ QCA_GPIO_PIN_FUNCTION(gpio), + QCA_PIN_FUNCTION(i2s_rx), + QCA_PIN_FUNCTION(i2s_spdif_in), + QCA_PIN_FUNCTION(i2s_spdif_out), +diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h +index 4625fa5320a95..120217012a9f6 100644 +--- a/drivers/pinctrl/qcom/pinctrl-msm.h ++++ b/drivers/pinctrl/qcom/pinctrl-msm.h +@@ -39,6 +39,11 @@ struct pinctrl_pin_desc; + fname##_groups, \ + ARRAY_SIZE(fname##_groups)) + ++#define QCA_GPIO_PIN_FUNCTION(fname) \ ++ [qca_mux_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \ ++ fname##_groups, \ ++ ARRAY_SIZE(fname##_groups)) ++ + /** + * struct msm_pingroup - Qualcomm pingroup definition + * @grp: Generic data of the pin group (name and pins) +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch b/queue-6.18/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch new file mode 100644 index 0000000000..6890226b20 --- /dev/null +++ b/queue-6.18/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch @@ -0,0 +1,49 @@ +From 6554348c091b331743f318db6671e681391ccb44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 09:05:45 +0000 +Subject: pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high + pins during suspend/resume +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Biju Das + +[ Upstream commit 6dba9b7268cc50166bce47608670192fd874e363 ] + +When saving/restoring pull-up/down register state during suspend/resume, +the second PUPD register access was incorrectly using the same base offset +as the first, effectively reading/writing the same register twice instead +of the adjacent one. + +Add the correct + 4 byte offset to the second RZG2L_PCTRL_REG_ACCESS32 +call so that pupd[1][port] is properly saved and restored from the next +32-bit register in the PUPD register pair, covering pins 4–7 of ports +with 4 or more pins. + +Fixes: b2bd65fbb617 ("pinctrl: renesas: rzg2l: Add suspend/resume support for pull up/down") +Signed-off-by: Biju Das +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +index ca360185740a8..df784e3807af0 100644 +--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c ++++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +@@ -3035,7 +3035,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), + cache->pupd[0][port]); + if (pincnt >= 4) { +- RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off) + 4, + cache->pupd[1][port]); + } + } +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch b/queue-6.18/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch new file mode 100644 index 0000000000..778dba4495 --- /dev/null +++ b/queue-6.18/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch @@ -0,0 +1,87 @@ +From 1ebe476548596e8f2a4e365346dd4182854eef9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Apr 2026 19:24:51 +0100 +Subject: pinctrl: renesas: rzg2l: Fix SMT register cache handling + +From: Lad Prabhakar + +[ Upstream commit c88ab9407986836820848128ce1f90f2fa49da95 ] + +Store SMT register cache per bank instead of using a single array. + +On RZ/V2H(P), RZ/V2N, and RZ/G3E, the SMT register is split across two +32-bit registers: bits 0/8/16/24 control pins 0-3, while pins 4-7 are +controlled by the corresponding bits in the next register. The previous +implementation cached only a single SMT register, leading to incomplete +save/restore of SMT state. + +Convert cache->smt to a per-bank array and allocate storage for both +halves. Update suspend/resume handling to save and restore both SMT +registers when present. + +Fixes: 837afa592c623 ("pinctrl: renesas: rzg2l: Add suspend/resume support for Schmitt control registers") +Signed-off-by: Lad Prabhakar +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260413182456.811543-2-prabhakar.mahadev-lad.rj@bp.renesas.com +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/renesas/pinctrl-rzg2l.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +index df784e3807af0..aaa783c79e6dd 100644 +--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c ++++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +@@ -335,7 +335,7 @@ struct rzg2l_pinctrl_reg_cache { + u32 *iolh[2]; + u32 *ien[2]; + u32 *pupd[2]; +- u32 *smt; ++ u32 *smt[2]; + u8 sd_ch[2]; + u8 eth_poc[2]; + u8 oen; +@@ -2723,10 +2723,6 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) + if (!cache->pfc) + return -ENOMEM; + +- cache->smt = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt), GFP_KERNEL); +- if (!cache->smt) +- return -ENOMEM; +- + for (u8 i = 0; i < 2; i++) { + u32 n_dedicated_pins = pctrl->data->n_dedicated_pins; + +@@ -2745,6 +2741,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) + if (!cache->pupd[i]) + return -ENOMEM; + ++ cache->smt[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt[i]), ++ GFP_KERNEL); ++ if (!cache->smt[i]) ++ return -ENOMEM; ++ + /* Allocate dedicated cache. */ + dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->iolh[i]), +@@ -3052,8 +3053,14 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen + } + } + +- if (has_smt) +- RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), cache->smt[port]); ++ if (has_smt) { ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), ++ cache->smt[0][port]); ++ if (pincnt >= 4) { ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off) + 4, ++ cache->smt[1][port]); ++ } ++ } + } + } + +-- +2.53.0 + diff --git a/queue-6.18/platform-surface-aggregator_registry-omit-battery-ac.patch b/queue-6.18/platform-surface-aggregator_registry-omit-battery-ac.patch new file mode 100644 index 0000000000..ba0c826a36 --- /dev/null +++ b/queue-6.18/platform-surface-aggregator_registry-omit-battery-ac.patch @@ -0,0 +1,47 @@ +From 45a1f679a55ef23cd2fcd7e3f627473d0b5dfc4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Apr 2026 15:43:47 +1200 +Subject: platform/surface: aggregator_registry: omit battery & AC nodes on + Surface Laptop 7 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Oliver White + +[ Upstream commit 0488073a6c84571dd3cffe581a4a73a5fceb099d ] + +Surface Laptop 7 exposes battery and AC status via Qualcomm PMIC GLINK +qcom_battmgr. Registering the standard SSAM battery and AC client +devices on this platform causes duplicate power-supply devices to +appear. + +Drop the SSAM battery and AC nodes from the Surface Laptop 7 registry +group so that only the qcom_battmgr power supplies are instantiated. + +Fixes: b27622f13172 ("platform/surface: Add OF support") +Signed-off-by: Oliver White +Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/surface/surface_aggregator_registry.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c +index a594d5fcfcfd1..d29158faba3e8 100644 +--- a/drivers/platform/surface/surface_aggregator_registry.c ++++ b/drivers/platform/surface/surface_aggregator_registry.c +@@ -295,8 +295,6 @@ static const struct software_node *ssam_node_group_sl6[] = { + /* Devices for Surface Laptop 7. */ + static const struct software_node *ssam_node_group_sl7[] = { + &ssam_node_root, +- &ssam_node_bat_ac, +- &ssam_node_bat_main, + &ssam_node_tmp_perf_profile_with_fan, + &ssam_node_fan_speed, + &ssam_node_hid_sam_keyboard, +-- +2.53.0 + diff --git a/queue-6.18/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-6.18/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..e9aa19a911 --- /dev/null +++ b/queue-6.18/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From 5afec3ce0ea5d957a828b411603a18288a54a0c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 6fa60f3fc53c0..8f7a26e6de81d 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-6.18/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-6.18/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..d9e6c3f8f8 --- /dev/null +++ b/queue-6.18/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From 7d50c77179e9306bdd41d13f9315ab5ff1c417fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 10d5af18d6398..39b73dc473f1c 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-6.18/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-6.18/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..20d1280570 --- /dev/null +++ b/queue-6.18/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From da2589763f9cabc03d57cebea4dc426ba9a6035d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index c5e80887d0cb0..f7e358be1af3a 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -682,12 +682,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-6.18/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-6.18/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..105701609e --- /dev/null +++ b/queue-6.18/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From f37db1e042d18cf4fe30dc9d38501314c778aa6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index 232cd12e3c9fa..03b5bf461a866 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -275,12 +275,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-6.18/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch b/queue-6.18/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch new file mode 100644 index 0000000000..7f48e953fe --- /dev/null +++ b/queue-6.18/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch @@ -0,0 +1,48 @@ +From c35022cd4f8fb768a143902ecbf7be213d9a4ec1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 16 Nov 2025 19:55:44 +0530 +Subject: powerpc: 82xx: fix uninitialized pointers with free attribute + +From: Ally Heev + +[ Upstream commit acd1e47db03d4b528fd5efb8565dd0de1c79f62a ] + +Uninitialized pointers with `__free` attribute can cause undefined +behavior as the memory allocated to the pointer is freed automatically +when the pointer goes out of scope. + +powerpc/km82xx doesn't have any bugs related to this as of now, but, +it is better to initialize and assign pointers with `__free` attribute +in one statement to ensure proper scope-based cleanup + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/ +Signed-off-by: Ally Heev +Fixes: 4aa5cc1e0012 ("powerpc-km82xx.c: replace of_node_put() with __free") +Reviewed-by: Christophe Leroy (CS GROUP) +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20251116-aheev-uninitialized-free-attr-km82xx-v2-1-4307e2b5300d@gmail.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/platforms/82xx/km82xx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c +index 99f0f0f418767..4ad223525e893 100644 +--- a/arch/powerpc/platforms/82xx/km82xx.c ++++ b/arch/powerpc/platforms/82xx/km82xx.c +@@ -27,8 +27,8 @@ + + static void __init km82xx_pic_init(void) + { +- struct device_node *np __free(device_node); +- np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); ++ struct device_node *np __free(device_node) = of_find_compatible_node(NULL, ++ NULL, "fsl,pq2-pic"); + + if (!np) { + pr_err("PIC init: can not find cpm-pic node\n"); +-- +2.53.0 + diff --git a/queue-6.18/powerpc-fix-dead-default-for-guest_state_buffer_test.patch b/queue-6.18/powerpc-fix-dead-default-for-guest_state_buffer_test.patch new file mode 100644 index 0000000000..72d9951035 --- /dev/null +++ b/queue-6.18/powerpc-fix-dead-default-for-guest_state_buffer_test.patch @@ -0,0 +1,54 @@ +From e4bb731e8b429749c13e1a17fe534c833beeada6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Apr 2026 17:15:45 +0100 +Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST + +From: Julian Braha + +[ Upstream commit aef656a0e6c01796190bb5bd2bdba1c644ed7811 ] + +The GUEST_STATE_BUFFER_TEST config option should default +to KUNIT_ALL_TESTS so that if all tests are enabled then +it is included, but currently the 'default KUNIT_ALL_TESTS' +statement is shadowed by 'def_tristate n', +meaning that this second default statement is currently dead code. + +It looks to me like the commit +6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +intended to set the default to KUNIT_ALL_TESTS, but mistakenly +missed the def_tristate. + +This dead code was found by kconfirm, a static analysis tool for Kconfig. + +Fixes: 6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +Signed-off-by: Julian Braha +Tested-by: Gautam Menghani +Reviewed-by: Amit Machhiwal +Reviewed-by: Harsh Prateek Bora +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/Kconfig.debug | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug +index f15e5920080ba..e8718bc13eeb1 100644 +--- a/arch/powerpc/Kconfig.debug ++++ b/arch/powerpc/Kconfig.debug +@@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST + depends on DEBUG_KERNEL + + config GUEST_STATE_BUFFER_TEST +- def_tristate n ++ def_tristate KUNIT_ALL_TESTS + prompt "Enable Guest State Buffer unit tests" + depends on KUNIT + depends on KVM_BOOK3S_HV_POSSIBLE +- default KUNIT_ALL_TESTS + help + The Guest State Buffer is a data format specified in the PAPR. + It is by hcalls to communicate the state of L2 guests between +-- +2.53.0 + diff --git a/queue-6.18/powerpc-time-remove-redundant-preempt_disable-enable.patch b/queue-6.18/powerpc-time-remove-redundant-preempt_disable-enable.patch new file mode 100644 index 0000000000..167f115479 --- /dev/null +++ b/queue-6.18/powerpc-time-remove-redundant-preempt_disable-enable.patch @@ -0,0 +1,98 @@ +From 21ac7b3c34689051a92354b937cc87c96a4c441f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 13:44:13 +0530 +Subject: powerpc/time: Remove redundant preempt_disable|enable() calls from + arch_irq_work_raise() + +From: Sayali Patil + +[ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] + +A kernel panic is observed when handling machine check exceptions from +real mode. + + BUG: Unable to handle kernel data access on read at 0xc00000006be21300 + Oops: Kernel access of bad area, sig: 11 [#1] + MSR: 8000000000001003 CR: 88222248 XER: 00000005 + CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 + NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 + LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 + Call Trace: + [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 + [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 + +The crash occurs because arch_irq_work_raise() calls preempt_disable() +from machine check exception (MCE) handlers running in real mode. In +this context, accessing the preempt_count can fault, leading to the panic. + +The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix +oops due to perf_event_do_pending call") to avoid races while raising +irq work from exception context. + +Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when +queueing work on the local CPU") added preemption protection in +irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per +cpu atomics instead of regular atomics") added equivalent +protection in irq_work_queue_on() before reaching arch_irq_work_raise(): + + irq_work_queue() / irq_work_queue_on() + -> preempt_disable() + -> __irq_work_queue_local() + -> irq_work_raise() + -> arch_irq_work_raise() + +As a result, callers other than mce_irq_work_raise() already execute +with preemption disabled, making the additional +preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +redundant. + +The arch_irq_work_raise() function executes in NMI context when called +from MCE handler. Hence we will not be preempted or scheduled out since +we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove +the preempt_disable()/preempt_enable() calls from here. + +Remove it to avoid accessing preempt_count from real mode context. + +Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") +Suggested-by: Mahesh Salgaonkar +Acked-by: Shrikanth Hegde +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Sayali Patil +[Maddy: Fixed the commit title] +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/kernel/time.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c +index 4bbeb8644d3da..b4472288e0d43 100644 +--- a/arch/powerpc/kernel/time.c ++++ b/arch/powerpc/kernel/time.c +@@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); + + #endif /* 32 vs 64 bit */ + ++/* ++ * Must be called with preemption disabled since it updates ++ * per-CPU irq_work state and programs the local CPU decrementer. ++ */ + void arch_irq_work_raise(void) + { + /* +@@ -471,10 +475,8 @@ void arch_irq_work_raise(void) + * which could get tangled up if we're messing with the same state + * here. + */ +- preempt_disable(); + set_irq_work_pending_flag(); + set_dec(1); +- preempt_enable(); + } + + static void set_dec_or_work(u64 val) +-- +2.53.0 + diff --git a/queue-6.18/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch b/queue-6.18/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch new file mode 100644 index 0000000000..de21147687 --- /dev/null +++ b/queue-6.18/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch @@ -0,0 +1,37 @@ +From f9cb3eef6f85d89d2983a668506e6c8adbc7412e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 02:42:09 -0700 +Subject: RDMA/mana_ib: Report max_msg_sz in mana_ib_query_port + +From: Shiraz Saleem + +[ Upstream commit c9a40f6531b81baa9619bcc2697ff86896afcce7 ] + +Report max_msg_sz for mana_ib, which is 16MB. + +Fixes: 4bda1d5332ec ("RDMA/mana_ib: Implement port parameters") +Signed-off-by: Shiraz Saleem +Signed-off-by: Konstantin Taranov +Link: https://patch.msgid.link/20260512094209.264955-1-kotaranov@linux.microsoft.com +Reviewed-by: Long Li +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/hw/mana/main.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c +index fac159f7128d9..4143be70eea20 100644 +--- a/drivers/infiniband/hw/mana/main.c ++++ b/drivers/infiniband/hw/mana/main.c +@@ -639,6 +639,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port, + if (mana_ib_is_rnic(dev)) { + props->gid_tbl_len = 16; + props->ip_gids = true; ++ props->max_msg_sz = SZ_16M; + if (port == 1) + props->port_cap_flags = IB_PORT_CM_SUP; + } +-- +2.53.0 + diff --git a/queue-6.18/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-6.18/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..141ec9c291 --- /dev/null +++ b/queue-6.18/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From f931e04d5571ee31fdacb25efcaaac3eedd7179f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 3f305e694fe8c..1b1c6ea4ee5a4 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.18/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch b/queue-6.18/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch new file mode 100644 index 0000000000..703af40b8d --- /dev/null +++ b/queue-6.18/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch @@ -0,0 +1,45 @@ +From d2f3d1b5978e9ce51c06617a1a83ebdee2bd81c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Apr 2026 09:11:39 +0000 +Subject: riscv: errata: Fix bitwise vs logical AND in MIPS errata patching + +From: Michael Neuling + +[ Upstream commit 4d2b03699460b8fd5df34408a03a84a1a7ff8aa1 ] + +The condition checking whether a specific errata needs patching uses +logical AND (&&) instead of bitwise AND (&). Since logical AND only +checks that both operands are non-zero, this causes all errata patches +to be applied whenever any single errata is detected, rather than only +applying the matching one. + +The SiFive errata implementation correctly uses bitwise AND for the same +check. + +Fixes: 0b0ca959d206 ("riscv: errata: Fix the PAUSE Opcode for MIPS P8700") +Signed-off-by: Michael Neuling +Assisted-by: Cursor:claude-4.6-opus-high-thinking +Link: https://patch.msgid.link/20260409091143.1348853-2-mikey@neuling.org +[pjw@kernel.org: fixed checkpatch warning] +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/errata/mips/errata.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/riscv/errata/mips/errata.c b/arch/riscv/errata/mips/errata.c +index e984a8152208c..2c3dc2259e93e 100644 +--- a/arch/riscv/errata/mips/errata.c ++++ b/arch/riscv/errata/mips/errata.c +@@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, + } + + tmp = (1U << alt->patch_id); +- if (cpu_req_errata && tmp) { ++ if (cpu_req_errata & tmp) { + mutex_lock(&text_mutex); + patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt), + alt->alt_len); +-- +2.53.0 + diff --git a/queue-6.18/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch b/queue-6.18/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch new file mode 100644 index 0000000000..4928c28979 --- /dev/null +++ b/queue-6.18/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch @@ -0,0 +1,87 @@ +From 45955e7aa2ee984561070d2b1ce585c295246384 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 25 Jan 2026 00:52:12 -0500 +Subject: riscv: mm: Fixup no5lvl failure when vaddr is invalid +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guo Ren (Alibaba DAMO Academy) + +[ Upstream commit db909bd7986c10da074917af3dae83a60fa65093 ] + +Unlike no4lvl, no5lvl still continues to detect satp, which +requires va=pa mapping. When pa=0x800000000000, no5lvl +would fail in Sv48 mode due to an illegal VA value of +0x800000000000. + +So, prevent detecting the satp flow for no5lvl, when +vaddr is invalid. Add the is_vaddr_valid() function for +checking. + +Fixes: 26e7aacb83df ("riscv: Allow to downgrade paging mode from the command line") +Cc: Alexandre Ghiti +Cc: Björn Töpel +Signed-off-by: Guo Ren (Alibaba DAMO Academy) +Tested-by: Fangyu Yu +Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org +[pjw@kernel.org: cleaned up commit message] +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index d85efe74a4b69..ee40ca01ac663 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -852,6 +852,27 @@ static void __init set_mmap_rnd_bits_max(void) + mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3; + } + ++static bool __init is_vaddr_valid(unsigned long va) ++{ ++ unsigned long up = 0; ++ ++ switch (satp_mode) { ++ case SATP_MODE_39: ++ up = 1UL << 38; ++ break; ++ case SATP_MODE_48: ++ up = 1UL << 47; ++ break; ++ case SATP_MODE_57: ++ up = 1UL << 56; ++ break; ++ default: ++ return false; ++ } ++ ++ return (va < up) || (va >= (ULONG_MAX - up + 1)); ++} ++ + /* + * There is a simple way to determine if 4-level is supported by the + * underlying hardware: establish 1:1 mapping in 4-level page table mode +@@ -893,6 +914,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + set_satp_mode_pmd + PMD_SIZE, + PMD_SIZE, PAGE_KERNEL_EXEC); + retry: ++ if (!is_vaddr_valid(set_satp_mode_pmd)) ++ goto out; ++ + create_pgd_mapping(early_pg_dir, + set_satp_mode_pmd, + pgtable_l5_enabled ? +@@ -915,6 +939,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + disable_pgtable_l4(); + } + ++out: + memset(early_pg_dir, 0, PAGE_SIZE); + memset(early_p4d, 0, PAGE_SIZE); + memset(early_pud, 0, PAGE_SIZE); +-- +2.53.0 + diff --git a/queue-6.18/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch b/queue-6.18/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch new file mode 100644 index 0000000000..0e1205f3b2 --- /dev/null +++ b/queue-6.18/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch @@ -0,0 +1,41 @@ +From 426be55e9338a46c1ceface0caf04cecd6e8c614 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 12:53:17 -0500 +Subject: scsi: sd: Fix return code handling in sd_spinup_disk() + +From: Mike Christie + +[ Upstream commit 6ea68a8dc7d2711504d944811981a5304af7d7a9 ] + +As found by smatch-ci, scsi_execute_cmd() can return negative or positve +values so we should use a int instead of unsigned int. + +Fixes: b4d0c33a32c3 ("scsi: sd: Fix sshdr use in sd_spinup_disk") +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u +Signed-off-by: Mike Christie +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/sd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c +index 072d4c4add334..d9bae23cb929e 100644 +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -2398,8 +2398,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) + { + static const u8 cmd[10] = { TEST_UNIT_READY }; + unsigned long spintime_expire = 0; +- int spintime, sense_valid = 0; +- unsigned int the_result; ++ int the_result, spintime, sense_valid = 0; + struct scsi_sense_hdr sshdr; + struct scsi_failure failure_defs[] = { + /* Do not retry Medium Not Present */ +-- +2.53.0 + diff --git a/queue-6.18/selftests-net-fix-checksums-in-xdp_native.patch b/queue-6.18/selftests-net-fix-checksums-in-xdp_native.patch new file mode 100644 index 0000000000..d9100e1171 --- /dev/null +++ b/queue-6.18/selftests-net-fix-checksums-in-xdp_native.patch @@ -0,0 +1,161 @@ +From 32b188f13184f1c2036d817e422a160b8c0bd627 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:39:28 +0300 +Subject: selftests: net: Fix checksums in xdp_native + +From: Nimrod Oren + +[ Upstream commit dfc077043351a81887d1e4c9ac244e9243f3cbf2 ] + +Data adjustment cases failed with "Data exchange failed" when using IPv4 +because the program did not update the IP and UDP checksums in the IPv4 +branch. The issue was masked when both IPv4 and IPv6 were configured, +since the test harness prefers IPv6. + +While here, generalize csum_fold_helper() to fold twice so it works for +any 32-bit input. + +Fixes: 0b65cfcef9c5 ("selftests: drv-net: Test tail-adjustment support") +Reviewed-by: Carolina Jubran +Reviewed-by: Dragos Tatulea +Signed-off-by: Nimrod Oren +Link: https://patch.msgid.link/20260520153928.3371765-1-noren@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../selftests/net/lib/xdp_native.bpf.c | 55 ++++++++++--------- + 1 file changed, 30 insertions(+), 25 deletions(-) + +diff --git a/tools/testing/selftests/net/lib/xdp_native.bpf.c b/tools/testing/selftests/net/lib/xdp_native.bpf.c +index c368fc045f4b4..d5134f93f7d7f 100644 +--- a/tools/testing/selftests/net/lib/xdp_native.bpf.c ++++ b/tools/testing/selftests/net/lib/xdp_native.bpf.c +@@ -268,6 +268,17 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port) + return XDP_PASS; + } + ++static __always_inline __u16 csum_fold_helper(__u32 csum) ++{ ++ csum = (csum & 0xffff) + (csum >> 16); ++ return ~((csum & 0xffff) + (csum >> 16)); ++} ++ ++static __always_inline __u16 csum_fold_udp_helper(__u32 csum) ++{ ++ return csum_fold_helper(csum) ? : 0xffff; ++} ++ + static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + { + void *data_end = (void *)(long)ctx->data_end; +@@ -281,21 +292,22 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = data + sizeof(*eth); +- __u16 total_len; + + if (iph + 1 > (struct iphdr *)data_end) + return NULL; + +- iph->tot_len = bpf_htons(bpf_ntohs(iph->tot_len) + offset); +- + udph = (void *)eth + sizeof(*iph) + sizeof(*eth); + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + +- len_new = bpf_htons(bpf_ntohs(udph->len) + offset); ++ len = iph->tot_len; ++ len_new = bpf_htons(bpf_ntohs(len) + offset); ++ iph->tot_len = len_new; ++ iph->check = csum_fold_helper( ++ bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), ~((__u32)iph->check))); + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6h = data + sizeof(*eth); +- __u16 payload_len; + + if (ipv6h + 1 > (struct ipv6hdr *)data_end) + return NULL; +@@ -304,33 +316,27 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + +- *udp_csum = ~((__u32)udph->check); +- + len = ipv6h->payload_len; + len_new = bpf_htons(bpf_ntohs(len) + offset); + ipv6h->payload_len = len_new; +- +- *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, +- sizeof(len_new), *udp_csum); +- +- len = udph->len; +- len_new = bpf_htons(bpf_ntohs(udph->len) + offset); +- *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, +- sizeof(len_new), *udp_csum); + } else { + return NULL; + } + ++ len = udph->len; ++ len_new = bpf_htons(bpf_ntohs(len) + offset); ++ ++ *udp_csum = ~((__u32)udph->check); ++ *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), *udp_csum); ++ *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), *udp_csum); ++ + udph->len = len_new; + + return udph; + } + +-static __u16 csum_fold_helper(__u32 csum) +-{ +- return ~((csum & 0xffff) + (csum >> 16)) ? : 0xffff; +-} +- + static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, + __u32 hdr_len) + { +@@ -359,7 +365,7 @@ static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (bpf_xdp_adjust_tail(ctx, 0 - offset) < 0) + return -1; +@@ -403,7 +409,7 @@ static int xdp_adjst_tail_grow_data(struct xdp_md *ctx, __u16 offset) + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)tmp_buff, offset, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + buff_len = bpf_xdp_get_buff_len(ctx); + +@@ -483,8 +489,7 @@ static int xdp_adjst_head_shrnk_data(struct xdp_md *ctx, __u64 hdr_len, + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); +- +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (bpf_xdp_load_bytes(ctx, 0, tmp_buff, MAX_ADJST_OFFSET) < 0) + return -1; +@@ -541,7 +546,7 @@ static int xdp_adjst_head_grow_data(struct xdp_md *ctx, __u64 hdr_len, + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)data_buff, offset, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0) + return -1; +-- +2.53.0 + diff --git a/queue-6.18/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch b/queue-6.18/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch new file mode 100644 index 0000000000..5101beedf4 --- /dev/null +++ b/queue-6.18/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch @@ -0,0 +1,61 @@ +From cd48f2a524f2d4b832531688f4cea56116cbf685 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 18:19:40 +0800 +Subject: selftests: ublk: cap nthreads to kernel's actual nr_hw_queues + +From: Ming Lei + +[ Upstream commit 87d0740b7c4cc847be1b6f307ab6d8547cb1a726 ] + +dev->nthreads is derived from the user-requested queue count before the +ADD command, but the kernel may reduce nr_hw_queues (capped to +nr_cpu_ids). When the VM has fewer CPUs than requested queues, the +daemon creates more handler threads than there are kernel queues. + +In non-batch mode, the extra threads access uninitialized queues +(q_depth=0), submit zero io_uring SQEs, and block forever in +io_cqring_wait. In batch mode, the extra threads cause similar hangs +during device removal. + +In both cases, the stuck threads prevent the daemon from closing the +char device, holding the last ublk_device reference and causing +ublk_ctrl_del_dev() to hang in wait_event_interruptible(). + +Fix by capping dev->nthreads to the kernel-returned nr_hw_queues after +the ADD command completes. per_io_tasks mode is excluded because threads +interleave across all queues, so nthreads > nr_hw_queues is valid. + +Fixes: abe54c160346 ("selftests: ublk: kublk: decouple ublk_queues from ublk server threads") +Signed-off-by: Ming Lei +Link: https://patch.msgid.link/20260513101941.1373998-1-tom.leiming@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/ublk/kublk.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c +index cbd23444c8a98..ac47979349a4b 100644 +--- a/tools/testing/selftests/ublk/kublk.c ++++ b/tools/testing/selftests/ublk/kublk.c +@@ -1220,6 +1220,17 @@ static int __cmd_dev_add(const struct dev_ctx *ctx) + goto fail; + } + ++ /* ++ * The kernel may reduce nr_hw_queues (e.g. capped to nr_cpu_ids). ++ * Cap nthreads to the actual queue count to avoid creating extra ++ * handler threads that will hang during device removal. ++ * ++ * per_io_tasks mode is excluded: threads interleave across all ++ * queues so nthreads > nr_hw_queues is valid and intentional. ++ */ ++ if (!ctx->per_io_tasks && dev->nthreads > info->nr_hw_queues) ++ dev->nthreads = info->nr_hw_queues; ++ + ret = ublk_start_daemon(ctx, dev); + ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\n", __func__, ret); + if (ret < 0) +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index 7b4bc54566..85c11bda82 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -167,3 +167,211 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch +arm-dts-renesas-genmai-drop-superfluous-cells.patch +arm-dts-renesas-rskrza1-drop-superfluous-cells.patch +pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch +pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch +pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch +pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch +hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch +hid-uclogic-fix-regression-of-input-name-assignment.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch +firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch +riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch +riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch +firmware-arm_ffa-bound-partition_info_get_regs-copie.patch +firmware-arm_ffa-keep-framework-rx-release-under-loc.patch +firmware-arm_ffa-validate-framework-notification-mes.patch +firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch +firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch +firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch +arm-integrator-fix-early-initialization.patch +alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch +alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch +btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-x_tables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch +test_kprobes-clear-kprobes-between-test-runs.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch +net-shaper-reject-reparenting-of-existing-nodes.patch +idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch +ice-fix-setting-rss-vsi-hash-for-e830.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +net-lan966x-avoid-unregistering-netdev-on-register-f.patch +net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +nfsd-fix-infinite-loop-in-layout-state-revocation.patch +asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch +asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch +fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch +fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch +fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch +irqchip-ath79-cpu-remove-unused-function.patch +ublk-reject-max_sectors-smaller-than-page_sectors-in.patch +nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch +irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch +nvme-fix-bio-leak-on-mapping-failure.patch +nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch +zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch +tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch +asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch +powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch +powerpc-fix-dead-default-for-guest_state_buffer_test.patch +netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch +netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch +netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch +netfs-fix-overrun-check-in-netfs_extract_user_iter.patch +netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch +netfs-defer-the-emission-of-trace_netfs_folio.patch +netfs-fix-streaming-write-being-overwritten.patch +netfs-fix-potential-deadlock-in-write-through-mode.patch +netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch +netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch +netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch +netfs-fix-leak-of-request-in-netfs_write_begin-error.patch +netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch +netfs-fix-partial-invalidation-of-streaming-write-fo.patch +netfs-fix-folio-private-handling-in-netfs_perform_wr.patch +netfs-fix-netfs_read_folio-to-wait-on-writeback.patch +netfs-afs-fix-write-skipping-in-dir-link-writepages.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch +wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch +net-shaper-flip-the-polarity-of-the-valid-flag.patch +net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch +net-shaper-reject-duplicate-leaves-in-group-request.patch +net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch +net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch +net-shaper-reject-handle-ids-exceeding-internal-bit-.patch +net-shaper-enforce-singleton-netdev-scope-with-id-0.patch +net-shaper-reject-queue-scope-handle-with-missing-id.patch +block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch +block-recompute-nr_integrity_segments-in-blk_insert_.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch +accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch +net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch +drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch +selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch +x86-mce-restore-mca-polling-interval-halving.patch +documentation-intel_pstate-fix-description-of-asymme.patch +drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +powerpc-time-remove-redundant-preempt_disable-enable.patch +net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +net-phy-dp83tc811-add-reading-of-abilities.patch +ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch +ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch +ovpn-fix-race-between-deleting-interface-and-adding-.patch +gcc-plugins-always-define-const_cast_gimple-and-cons.patch +x86-xen-fix-xen_e820_swap_entry_with_ram.patch +ovpn-disable-bhs-when-updating-device-stats.patch +tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch +net-mlx5-do-not-restore-destination-less-tc-rules.patch +scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch +asoc-codecs-fs210x-fix-possible-buffer-overflow.patch +alsa-scarlett2-add-missing-error-check-when-initiali.patch +io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch +vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch +btrfs-add-macros-to-facilitate-printing-of-keys.patch +btrfs-use-the-key-format-macros-when-printing-keys.patch +btrfs-don-t-search-back-for-dir-inode-item-in-ino_lo.patch +btrfs-remaining-btrfs_path_auto_free-conversions.patch +btrfs-check-squota-parent-usage-on-membership-change.patch +btrfs-relax-squota-parent-qgroup-deletion-rule.patch +btrfs-check-for-subvolume-before-deleting-squota-qgr.patch +btrfs-fix-squota-accounting-during-enable-generation.patch +asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch +spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch +netfilter-nft_inner-release-local_lock-before-re-ena.patch +alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +hwmon-lm90-stop-work-before-releasing-hwmon-device.patch +hwmon-lm90-add-lock-protection-to-lm90_alert.patch +wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch +wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch +dma-mapping-move-dma_map_resource-sanity-check-into-.patch +drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch +drm-xe-vf-fix-signature-of-print-functions.patch +drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch +wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch +drm-mediatek-mtk_cec-fix-non-static-global-variable.patch +drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch +cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch +ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch +ice-ptp-use-primary-nac-semaphore-on-e825.patch +igc-set-tx-buffer-type-for-smd-frames.patch +drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch +kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-surface-aggregator_registry-omit-battery-ac.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch +rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +net-bridge-flush-multicast-groups-when-snooping-is-d.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch +net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch +net-airoha-fix-npu-rx-dma-descriptor-bits.patch +pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch +pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch +erofs-fix-managed-cache-race-for-unaligned-extents.patch +wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch +wifi-mac80211-fix-mle-defragmentation.patch +wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch +alsa-seq-serialize-ump-output-teardown-with-event_in.patch +cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch +bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch +net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch +net-shaper-annotate-the-data-races.patch +net-shaper-rework-the-valid-marking-again.patch +crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch +net-ag71xx-check-error-for-platform_get_irq.patch +bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch +tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +gpio-aggregator-fix-a-potential-use-after-free.patch +gpio-aggregator-stop-using-dev-sync-probe.patch +gpio-aggregator-remove-the-software-node-when-deacti.patch +gpio-aggregator-lock-device-when-calling-device_is_b.patch +asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch +drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch +selftests-net-fix-checksums-in-xdp_native.patch +octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch +tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch +net-airoha-disable-gdm2-forwarding-before-configurin.patch +pds_core-ensure-null-termination-for-firmware-versio.patch +net-gro-don-t-merge-zcopy-skbs.patch +io_uring-nop-pass-all-errors-to-userspace.patch +ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch +loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch diff --git a/queue-6.18/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch b/queue-6.18/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch new file mode 100644 index 0000000000..1e11e77906 --- /dev/null +++ b/queue-6.18/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch @@ -0,0 +1,38 @@ +From 67a8d4fcfaa6ec654274ae6c56b11cfcebb6e28c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 01:55:37 +0800 +Subject: spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() + +From: Felix Gu + +[ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] + +When DMA read times out in mtk_snand_read_page_cache(), the original code +erroneously jumped to cleanup label which skips DMA unmapping and ECC +disable, causing a resource leak. + +Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") +Signed-off-by: Felix Gu +Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-mtk-snfi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c +index a026b0e61994b..9e6038a5a2ad0 100644 +--- a/drivers/spi/spi-mtk-snfi.c ++++ b/drivers/spi/spi-mtk-snfi.c +@@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; +- goto cleanup; ++ goto cleanup2; + } + + // Wait for BUS_SEC_CNTR returning expected value +-- +2.53.0 + diff --git a/queue-6.18/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch b/queue-6.18/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch new file mode 100644 index 0000000000..c140864975 --- /dev/null +++ b/queue-6.18/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch @@ -0,0 +1,51 @@ +From e2383041f2064b676fd68c58b218aa5c62a3072f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 00:57:38 -0700 +Subject: tap: fix stack info leak in tap_ioctl() SIOCGIFHWADDR + +From: Weiming Shi + +[ Upstream commit bddc09212c24934643bd44fc794748d2bbb3b6cd ] + +In the SIOCGIFHWADDR path, tap_ioctl() copies 16 bytes of an +uninitialised on-stack struct sockaddr_storage to userspace via +ifr_hwaddr, but netif_get_mac_address() only writes sa_family and +dev->addr_len (6 for Ethernet) bytes, leaving sa_data[6..13] uninitialised. + +Those 8 trailing bytes leak kernel stack contents; SIOCGIFHWADDR on a +macvtap chardev returns kernel .text and direct-map pointers, defeating +KASLR. + +Initialise ss at declaration. + +Fixes: 3b23a32a6321 ("net: fix dev_ifsioc_locked() race condition") +Reported-by: Xiang Mei +Signed-off-by: Weiming Shi +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/20260520075736.3415676-3-bestswngs@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/tap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/tap.c b/drivers/net/tap.c +index 1197f245e8737..6fd3b14273b37 100644 +--- a/drivers/net/tap.c ++++ b/drivers/net/tap.c +@@ -919,11 +919,11 @@ static long tap_ioctl(struct file *file, unsigned int cmd, + struct tap_queue *q = file->private_data; + struct tap_dev *tap; + void __user *argp = (void __user *)arg; ++ struct sockaddr_storage ss = {}; + struct ifreq __user *ifr = argp; + unsigned int __user *up = argp; + unsigned short u; + int __user *sp = argp; +- struct sockaddr_storage ss; + int s; + int ret; + +-- +2.53.0 + diff --git a/queue-6.18/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-6.18/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..4fc9a7fcb6 --- /dev/null +++ b/queue-6.18/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From 59ff0e104eeddf582c1f06290077c82124f9b4ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index ed773cd488769..c777895a720ee 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1149,7 +1149,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-6.18/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch b/queue-6.18/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch new file mode 100644 index 0000000000..800ddd6518 --- /dev/null +++ b/queue-6.18/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch @@ -0,0 +1,52 @@ +From df967edce7a517b69c084c1558048f7b3fb0ab45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 12:08:46 +0000 +Subject: tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key(). + +From: Kuniyuki Iwashima + +[ Upstream commit 03cb001ef87b3f8d859cf7f96329acf3d6235d29 ] + +lockdep_sock_is_held() was added in tcp_ao_established_key() +by the cited commit. + +It can be called from tcp_v[46]_timewait_ack() with twsk. + +Since it does not have sk->sk_lock, the lockdep annotation +results in out-of-bound access. + + $ pahole -C tcp_timewait_sock vmlinux | grep size + /* size: 288, cachelines: 5, members: 8 */ + $ pahole -C sock vmlinux | grep sk_lock + socket_lock_t sk_lock; /* 440 192 */ + +Let's not use lockdep_sock_is_held() for TCP_TIME_WAIT. + +Fixes: 6b2d11e2d8fc ("net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/tcp_ao.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c +index 849a69c1f497f..aa624434b5556 100644 +--- a/net/ipv4/tcp_ao.c ++++ b/net/ipv4/tcp_ao.c +@@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, + { + struct tcp_ao_key *key; + +- hlist_for_each_entry_rcu(key, &ao->head, node, lockdep_sock_is_held(sk)) { ++ hlist_for_each_entry_rcu(key, &ao->head, node, ++ sk_fullsock(sk) && lockdep_sock_is_held(sk)) { + if ((sndid >= 0 && key->sndid != sndid) || + (rcvid >= 0 && key->rcvid != rcvid)) + continue; +-- +2.53.0 + diff --git a/queue-6.18/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch b/queue-6.18/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch new file mode 100644 index 0000000000..2783a3406c --- /dev/null +++ b/queue-6.18/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch @@ -0,0 +1,199 @@ +From 08c693ce95054aea3da80ce3b27a29e13d0ac03a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 08:46:11 +0000 +Subject: tcp: fix stale per-CPU tcp_tw_isn leak enabling ISN prediction + +From: Eric Dumazet + +[ Upstream commit 1bbf0ced1d9db73ac7893c2187f3459288603e0d ] + +Blamed commit moved the TIME_WAIT-derived ISN from the skb control +block to a per-CPU variable, assuming the value would always be consumed +by tcp_conn_request() for the same packet that wrote it. That assumption +is violated by multiple drop paths between the producer +(__this_cpu_write(tcp_tw_isn, isn) in tcp_v{4,6}_rcv()) and the consumer +(tcp_conn_request()): + + - min_ttl / min_hopcount check + - xfrm policy check + - tcp_inbound_hash() MD5/AO mismatch + - tcp_filter() eBPF/SO_ATTACH_FILTER drop + - th->syn && th->fin discard in tcp_rcv_state_process() TCP_LISTEN + - psp_sk_rx_policy_check() in tcp_v{4,6}_do_rcv() + - tcp_checksum_complete() in tcp_v{4,6}_do_rcv() + - tcp_v{4,6}_cookie_check() returning NULL + +When a packet is dropped on any of these paths, tcp_tw_isn is left set. + +The next SYN processed on the same CPU then consumes the non zero value in +tcp_conn_request(), receiving a potentially predictable ISN. + +This patch moves back tcp_tw_isn to skb->cb[], getting rid of the per-cpu +variable. + +Note that tcp_v{4,6}_fill_cb() do not set it. + +Very litle impact on overall code size/complexity: + +$ scripts/bloat-o-meter -t vmlinux.old vmlinux.new +add/remove: 0/0 grow/shrink: 2/1 up/down: 8/-15 (-7) +Function old new delta +tcp_v6_rcv 3038 3042 +4 +tcp_v4_rcv 3035 3039 +4 +tcp_conn_request 2938 2923 -15 +Total: Before=24436060, After=24436053, chg -0.00% + +Fixes: 41eecbd712b7 ("tcp: replace TCP_SKB_CB(skb)->tcp_tw_isn with a per-cpu field") +Reported-by: Chris Mason +Signed-off-by: Eric Dumazet +Reviewed-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260519084611.2485277-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/tcp.h | 7 ++++--- + net/ipv4/tcp.c | 3 --- + net/ipv4/tcp_input.c | 15 ++++++--------- + net/ipv4/tcp_ipv4.c | 3 ++- + net/ipv6/tcp_ipv6.c | 3 ++- + 5 files changed, 14 insertions(+), 17 deletions(-) + +diff --git a/include/net/tcp.h b/include/net/tcp.h +index cf507b989bff1..f460d2c391deb 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -65,8 +65,6 @@ static inline void tcp_orphan_count_dec(void) + this_cpu_dec(tcp_orphan_count); + } + +-DECLARE_PER_CPU(u32, tcp_tw_isn); +- + void tcp_time_wait(struct sock *sk, int state, int timeo); + + #define MAX_TCP_HEADER L1_CACHE_ALIGN(128 + MAX_HEADER) +@@ -1028,10 +1026,13 @@ struct tcp_skb_cb { + __u32 seq; /* Starting sequence number */ + __u32 end_seq; /* SEQ + FIN + SYN + datalen */ + union { +- /* Note : ++ /* Notes : ++ * tcp_tw_isn is used in input path only ++ * (isn chosen by tcp_timewait_state_process()) + * tcp_gso_segs/size are used in write queue only, + * cf tcp_skb_pcount()/tcp_skb_mss() + */ ++ u32 tcp_tw_isn; + struct { + u16 tcp_gso_segs; + u16 tcp_gso_size; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 2de4748269ca9..6fc00b38695ba 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -300,9 +300,6 @@ enum { + DEFINE_PER_CPU(unsigned int, tcp_orphan_count); + EXPORT_PER_CPU_SYMBOL_GPL(tcp_orphan_count); + +-DEFINE_PER_CPU(u32, tcp_tw_isn); +-EXPORT_PER_CPU_SYMBOL_GPL(tcp_tw_isn); +- + long sysctl_tcp_mem[3] __read_mostly; + EXPORT_IPV6_MOD(sysctl_tcp_mem); + +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index b5cf32a56c04a..c650398e91998 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -7387,6 +7387,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, + struct sock *sk, struct sk_buff *skb) + { + struct tcp_fastopen_cookie foc = { .len = -1 }; ++ u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn; + struct tcp_options_received tmp_opt; + const struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); +@@ -7397,20 +7398,16 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, + struct dst_entry *dst; + struct flowi fl; + u8 syncookies; +- u32 isn; + + #ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; + #endif + +- isn = __this_cpu_read(tcp_tw_isn); +- if (isn) { +- /* TW buckets are converted to open requests without +- * limitations, they conserve resources and peer is +- * evidently real one. +- */ +- __this_cpu_write(tcp_tw_isn, 0); +- } else { ++ /* If isn is non-zero, this SYN originally matched a TIME_WAIT socket. ++ * TW sockets are converted to open requests without limitations, ++ * we skip the queue limits and syncookie checks in the block below. ++ */ ++ if (!isn) { + syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); + + if (syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) { +diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c +index 702fdff58f7a3..36206fc6aed2d 100644 +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -2333,6 +2333,7 @@ int tcp_v4_rcv(struct sk_buff *skb) + } + } + ++ isn = 0; + process: + if (static_branch_unlikely(&ip4_min_ttl)) { + /* min_ttl can be changed concurrently from do_ip_setsockopt() */ +@@ -2361,6 +2362,7 @@ int tcp_v4_rcv(struct sk_buff *skb) + th = (const struct tcphdr *)skb->data; + iph = ip_hdr(skb); + tcp_v4_fill_cb(skb, iph, th); ++ TCP_SKB_CB(skb)->tcp_tw_isn = isn; + + skb->dev = NULL; + +@@ -2446,7 +2448,6 @@ int tcp_v4_rcv(struct sk_buff *skb) + sk = sk2; + tcp_v4_restore_cb(skb); + refcounted = false; +- __this_cpu_write(tcp_tw_isn, isn); + goto process; + } + +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index 7f20db11e8ce9..59b5900dd42bd 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -1863,6 +1863,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + } + } + ++ isn = 0; + process: + if (static_branch_unlikely(&ip6_min_hopcount)) { + /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ +@@ -1891,6 +1892,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + th = (const struct tcphdr *)skb->data; + hdr = ipv6_hdr(skb); + tcp_v6_fill_cb(skb, hdr, th); ++ TCP_SKB_CB(skb)->tcp_tw_isn = isn; + + skb->dev = NULL; + +@@ -1978,7 +1980,6 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + sk = sk2; + tcp_v6_restore_cb(skb); + refcounted = false; +- __this_cpu_write(tcp_tw_isn, isn); + goto process; + } + +-- +2.53.0 + diff --git a/queue-6.18/test_kprobes-clear-kprobes-between-test-runs.patch b/queue-6.18/test_kprobes-clear-kprobes-between-test-runs.patch new file mode 100644 index 0000000000..920c65b1fb --- /dev/null +++ b/queue-6.18/test_kprobes-clear-kprobes-between-test-runs.patch @@ -0,0 +1,128 @@ +From 09dde48718c984003f628ef1af8511dc2df0f13d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: test_kprobes: clear kprobes between test runs + +From: Martin Kaiser + +[ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ] + +Running the kprobes sanity tests twice makes all tests fail and +eventually crashes the kernel. + +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # Totals: pass:5 fail:0 skip:0 total:5 + ok 1 kprobes_test +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 + Expected 0 == register_kprobe(&kp), but + register_kprobe(&kp) == -22 (0xffffffffffffffea) +... + Unable to handle kernel paging request ... + +The testsuite defines several kprobes and kretprobes as static variables +that are preserved across test runs. + +After register_kprobe and unregister_kprobe, a kprobe contains some +leftover data that must be cleared before the kprobe can be registered +again. The tests are setting symbol_name to define the probe location. +Address and flags must be cleared. + +The existing code clears some of the probes between subsequent tests, but +not between two test runs. The leftover data from a previous test run +makes the registrations fail in the next run. + +Move the cleanups for all kprobes into kprobes_test_init, this function +is called before each single test (including the first test of a test +run). + +Link: https://lore.kernel.org/all/20260507134615.1010905-1-martin@kaiser.cx/ + +Fixes: e44e81c5b90f ("kprobes: convert tests to kunit") +Signed-off-by: Martin Kaiser +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + lib/tests/test_kprobes.c | 29 ++++++++++++++++++----------- + 1 file changed, 18 insertions(+), 11 deletions(-) + +diff --git a/lib/tests/test_kprobes.c b/lib/tests/test_kprobes.c +index b7582010125c3..06e729e4de051 100644 +--- a/lib/tests/test_kprobes.c ++++ b/lib/tests/test_kprobes.c +@@ -12,6 +12,12 @@ + + #define div_factor 3 + ++#define KP_CLEAR(_kp) \ ++do { \ ++ (_kp).addr = NULL; \ ++ (_kp).flags = 0; \ ++} while (0) ++ + static u32 rand1, preh_val, posth_val; + static u32 (*target)(u32 value); + static u32 (*recursed_target)(u32 value); +@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) + + current_test = test; + +- /* addr and flags should be cleard for reusing kprobe. */ +- kp.addr = NULL; +- kp.flags = 0; +- + KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); + preh_val = 0; + posth_val = 0; +@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) + struct kretprobe *rps[2] = {&rp, &rp2}; + + current_test = test; +- /* addr and flags should be cleard for reusing kprobe. */ +- rp.kp.addr = NULL; +- rp.kp.flags = 0; + KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); + + krph_val = 0; +@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) + unsigned long myretaddr = (unsigned long)__builtin_return_address(0); + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + /* + * Run the stacktrace_driver() to record correct return address in +@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + struct kretprobe *rps[2] = {&rp3, &rp4}; + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); + +@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + + static int kprobes_test_init(struct kunit *test) + { ++ KP_CLEAR(kp); ++ KP_CLEAR(kp2); ++ KP_CLEAR(kp_missed); ++#ifdef CONFIG_KRETPROBES ++ KP_CLEAR(rp.kp); ++ KP_CLEAR(rp2.kp); ++#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE ++ KP_CLEAR(rp3.kp); ++ KP_CLEAR(rp4.kp); ++#endif ++#endif ++ + target = kprobe_target; + target2 = kprobe_target2; + recursed_target = kprobe_recursed_target; +-- +2.53.0 + diff --git a/queue-6.18/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch b/queue-6.18/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch new file mode 100644 index 0000000000..46c98fab4d --- /dev/null +++ b/queue-6.18/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch @@ -0,0 +1,125 @@ +From 3b31acc672fe7ffce1b08da296ba10eda3dafd88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 08:58:25 -0400 +Subject: tls: Preserve sk_err across recvmsg() when data has been copied + +From: Chuck Lever + +[ Upstream commit f508262ae9f21fe0e6c0749948b9dc7dd5a62a70 ] + +The sk_err check in tls_rx_rec_wait() consumes the error via +sock_error(), which clears sk_err atomically. When the caller +(tls_sw_recvmsg, tls_sw_splice_read, or tls_sw_read_sock) already +has bytes copied to userspace, it returns those bytes and discards +the error from this call. sk_err is now zero on the socket, so the +next read syscall observes only RCV_SHUTDOWN and reports a clean +EOF instead of the actual error (typically -ECONNRESET). + +The race is reachable when tls_read_flush_backlog()'s periodic +sk_flush_backlog() triggers tcp_reset() in the middle of a +multi-record read. + +Pass a has_copied flag to tls_rx_rec_wait(). When has_copied is +false, consume sk_err via sock_error() as before. When has_copied +is true, report the error from READ_ONCE() but leave sk_err set: +the caller returns the byte count and discards the err from this +call, and the next read syscall surfaces the preserved sk_err. This +mirrors the tcp_recvmsg() preserve-and-surface pattern. + +The decrypt-abort path is unaffected: tls_err_abort() raises +sk_err to EBADMSG after tls_rx_rec_wait() returns, and nothing +on the caller's return path consumes it, so the EBADMSG surfaces +on the next read. + +tls_sw_splice_read() passes has_copied=false: it processes +one record per call, so no bytes have been copied within the +function when tls_rx_rec_wait() runs. A reset that arrives +between iterations of splice_direct_to_actor() (the sendfile() +path) is still consumed by sock_error() in the later call, and the +outer loop returns the prior iterations' byte count and drops the +error. tcp_splice_read() exhibits the same pattern at the iteration +boundary; addressing it belongs at the splice_direct_to_actor() +layer and is out of scope here. + +Fixes: c46b01839f7a ("tls: rx: periodically flush socket backlog") +Suggested-by: Jakub Kicinski +Signed-off-by: Chuck Lever +Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index b28eb04075d1b..034f322054e53 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -1366,9 +1366,14 @@ void tls_sw_splice_eof(struct socket *sock) + mutex_unlock(&tls_ctx->tx_lock); + } + ++/* When has_copied is true the caller has already moved bytes to ++ * userspace. Report sk_err but leave it set so the next read ++ * surfaces it instead of a spurious EOF, otherwise sk_err is ++ * consumed via sock_error(). ++ */ + static int + tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, +- bool released) ++ bool released, bool has_copied) + { + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); +@@ -1386,8 +1391,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + if (!sk_psock_queue_empty(psock)) + return 0; + +- if (sk->sk_err) ++ if (sk->sk_err) { ++ if (has_copied) ++ return -READ_ONCE(sk->sk_err); + return sock_error(sk); ++ } + + if (ret < 0) + return ret; +@@ -1423,7 +1431,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + } + + if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) +- return tls_rx_rec_wait(sk, psock, nonblock, false); ++ return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); + + return 1; + } +@@ -2111,7 +2119,7 @@ int tls_sw_recvmsg(struct sock *sk, + int to_decrypt, chunk; + + err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, +- released); ++ released, !!(decrypted + copied)); + if (err <= 0) { + if (psock) { + chunk = sk_msg_recvmsg(sk, psock, msg, len, +@@ -2298,7 +2306,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct tls_decrypt_arg darg; + + err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, +- true); ++ true, false); + if (err <= 0) + goto splice_read_end; + +@@ -2384,7 +2392,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, + } else { + struct tls_decrypt_arg darg; + +- err = tls_rx_rec_wait(sk, NULL, true, released); ++ err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); + if (err <= 0) + goto read_sock_end; + +-- +2.53.0 + diff --git a/queue-6.18/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-6.18/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..b5826a333f --- /dev/null +++ b/queue-6.18/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 25c494e5770f5653579bcf75ac4debe52ef6ebc6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index cb9e067138683..0089c257b465f 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1360,10 +1360,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-6.18/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch b/queue-6.18/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch new file mode 100644 index 0000000000..1f05b5d07b --- /dev/null +++ b/queue-6.18/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch @@ -0,0 +1,52 @@ +From 79278407564a7a7b910401468a2b79630f91aef8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 22:48:43 +0800 +Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter + validation + +From: Ming Lei + +[ Upstream commit 1860c2f85922917d8a46f16a6f4bd2298ffa0fb5 ] + +blk_validate_limits() requires max_hw_sectors >= PAGE_SECTORS and fires +a WARN_ON_ONCE if this invariant is violated. ublk_validate_params() +only checked the upper bound of max_sectors against max_io_buf_bytes, +allowing userspace to pass small values (including zero) that trigger +the warning when blk_mq_alloc_disk() is called from +ublk_ctrl_start_dev(). + +Before 494ea040bcb5, ublk used blk_queue_max_hw_sectors() which silently +clamped small values up to PAGE_SECTORS. The conversion to passing +queue_limits directly to blk_mq_alloc_disk() lost that clamping and now +hits blk_validate_limits()'s WARN_ON_ONCE instead. + +Validate that max_sectors is at least PAGE_SECTORS in +ublk_validate_params() so invalid values are rejected early with +-EINVAL instead of reaching the block layer. + +Fixes: 494ea040bcb5 ("ublk: pass queue_limits to blk_mq_alloc_disk") +Signed-off-by: Ming Lei +Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + drivers/block/ublk_drv.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index 2729b1556e810..c339222513b03 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -601,6 +601,9 @@ static int ublk_validate_params(const struct ublk_device *ub) + if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) + return -EINVAL; + ++ if (p->max_sectors < PAGE_SECTORS) ++ return -EINVAL; ++ + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) + return -EINVAL; + } else +-- +2.53.0 + diff --git a/queue-6.18/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch b/queue-6.18/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch new file mode 100644 index 0000000000..c513d1db21 --- /dev/null +++ b/queue-6.18/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch @@ -0,0 +1,168 @@ +From ff7c3e800a28ca46626c870ebb32ed8495af387e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 11:29:48 +0200 +Subject: vsock/virtio: fix zerocopy completion for multi-skb sends + +From: Stefano Garzarella + +[ Upstream commit ae38d9179190a956e2a87a69ef1dd6f451b51c4d ] + +When a large message is fragmented into multiple skbs, the zerocopy +uarg is only allocated and attached to the last skb in the loop. +Non-final skbs carry pinned user pages with no completion tracking, +so the kernel has no way to notify userspace when those pages are safe +to reuse. If the loop breaks early the uarg is never allocated at all, +leaking pinned pages with no completion notification. + +Fix this by following the approach used by TCP: allocate the zerocopy +uarg (if not provided by the caller) before the send loop and attach +it to every skb via skb_zcopy_set(), which takes a reference per skb. +Each skb's completion properly decrements the refcount, and the +notification only fires after the last skb is freed. +On failure, if no data was sent, the uarg is cleanly aborted via +net_zcopy_put_abort(). + +This issue was initially discovered by sashiko while reviewing commit +1cb36e252211 ("vsock/virtio: fix MSG_ZEROCOPY pinned-pages accounting") +but was pre-existing. + +Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support") +Closes: https://sashiko.dev/#/patchset/20260420132051.217589-1-sgarzare%40redhat.com +Reported-by: Maher Azzouzi +Signed-off-by: Stefano Garzarella +Acked-by: Michael S. Tsirkin +Acked-by: Arseniy Krasnov +Link: https://patch.msgid.link/20260514092948.268720-1-sgarzare@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/vmw_vsock/virtio_transport_common.c | 83 ++++++++++--------------- + 1 file changed, 34 insertions(+), 49 deletions(-) + +diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c +index 65e2b24892346..ed42e08798a96 100644 +--- a/net/vmw_vsock/virtio_transport_common.c ++++ b/net/vmw_vsock/virtio_transport_common.c +@@ -72,34 +72,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops, + return true; + } + +-static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, +- struct sk_buff *skb, +- struct msghdr *msg, +- size_t pkt_len, +- bool zerocopy) +-{ +- struct ubuf_info *uarg; +- +- if (msg->msg_ubuf) { +- uarg = msg->msg_ubuf; +- net_zcopy_get(uarg); +- } else { +- struct ubuf_info_msgzc *uarg_zc; +- +- uarg = msg_zerocopy_realloc(sk_vsock(vsk), +- pkt_len, NULL, false); +- if (!uarg) +- return -1; +- +- uarg_zc = uarg_to_msgzc(uarg); +- uarg_zc->zerocopy = zerocopy ? 1 : 0; +- } +- +- skb_zcopy_init(skb, uarg); +- +- return 0; +-} +- + static int virtio_transport_fill_skb(struct sk_buff *skb, + struct virtio_vsock_pkt_info *info, + size_t len, +@@ -319,8 +291,10 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + u32 src_cid, src_port, dst_cid, dst_port; + const struct virtio_transport *t_ops; + struct virtio_vsock_sock *vvs; ++ struct ubuf_info *uarg = NULL; + u32 pkt_len = info->pkt_len; + bool can_zcopy = false; ++ bool have_uref = false; + u32 rest_len; + int ret; + +@@ -362,6 +336,25 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + if (can_zcopy) + max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE, + (MAX_SKB_FRAGS * PAGE_SIZE)); ++ ++ if (info->msg->msg_flags & MSG_ZEROCOPY && ++ info->op == VIRTIO_VSOCK_OP_RW) { ++ uarg = info->msg->msg_ubuf; ++ ++ if (!uarg) { ++ uarg = msg_zerocopy_realloc(sk_vsock(vsk), ++ pkt_len, NULL, false); ++ if (!uarg) { ++ virtio_transport_put_credit(vvs, pkt_len); ++ return -ENOMEM; ++ } ++ ++ if (!can_zcopy) ++ uarg_to_msgzc(uarg)->zerocopy = 0; ++ ++ have_uref = true; ++ } ++ } + } + + rest_len = pkt_len; +@@ -380,27 +373,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + break; + } + +- /* We process buffer part by part, allocating skb on +- * each iteration. If this is last skb for this buffer +- * and MSG_ZEROCOPY mode is in use - we must allocate +- * completion for the current syscall. +- * +- * Pass pkt_len because msg iter is already consumed +- * by virtio_transport_fill_skb(), so iter->count +- * can not be used for RLIMIT_MEMLOCK pinned-pages +- * accounting done by msg_zerocopy_realloc(). +- */ +- if (info->msg && info->msg->msg_flags & MSG_ZEROCOPY && +- skb_len == rest_len && info->op == VIRTIO_VSOCK_OP_RW) { +- if (virtio_transport_init_zcopy_skb(vsk, skb, +- info->msg, +- pkt_len, +- can_zcopy)) { +- kfree_skb(skb); +- ret = -ENOMEM; +- break; +- } +- } ++ skb_zcopy_set(skb, uarg, NULL); + + virtio_transport_inc_tx_pkt(vvs, skb); + +@@ -424,6 +397,18 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + + virtio_transport_put_credit(vvs, rest_len); + ++ /* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1. ++ * skb_zcopy_set() increases it for each skb, so we can drop that ++ * initial reference to keep it balanced. ++ */ ++ if (have_uref) { ++ if (rest_len == pkt_len) ++ /* No data sent, abort the notification. */ ++ net_zcopy_put_abort(uarg, true); ++ else ++ net_zcopy_put(uarg); ++ } ++ + /* Return number of bytes, if any data has been sent. */ + if (rest_len != pkt_len) + ret = pkt_len - rest_len; +-- +2.53.0 + diff --git a/queue-6.18/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch b/queue-6.18/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch new file mode 100644 index 0000000000..0d93e4e225 --- /dev/null +++ b/queue-6.18/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch @@ -0,0 +1,78 @@ +From c736755540f322167f32046402bed12c7b10f437 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 14:17:37 +0800 +Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged + +From: Kang Yang + +[ Upstream commit 54a5b38e4396530e5b2f12b54d3844e860ab6784 ] + +In ath10k_wmi_cmd_send(), the current code detects ATH10K_STATE_WEDGED +and sets ret to -ESHUTDOWN, but still proceeds to transmit pending +beacons and calls ath10k_wmi_cmd_send_nowait(). + +This can lead to incorrect behavior, as WMI commands and beacons are +still sent after the device has been marked as wedged, and the original +-ESHUTDOWN return value may be overwritten by the result of the send +path. + +The wedged state indicates the hardware is already unreliable, and no +further interaction with firmware is expected or meaningful in this +state. + +Fix this by skipping beacon transmission and the WMI send path entirely +once ATH10K_STATE_WEDGED is detected, ensuring consistent return values +and avoiding unnecessary firmware interaction. + +Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1 +Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189 + +Fixes: c256a94d1b1b ("wifi: ath10k: shutdown driver when hardware is unreliable") +Signed-off-by: Kang Yang +Reviewed-by: Rameshkumar Sundaram +Reviewed-by: Baochen Qiang +Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath10k/wmi.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c +index ce22141e5efd9..cd20508399b9a 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -3,7 +3,6 @@ + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +@@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) + ret = -ESHUTDOWN; + ath10k_dbg(ar, ATH10K_DBG_WMI, + "drop wmi command %d, hardware is wedged\n", cmd_id); +- } +- /* try to send pending beacons first. they take priority */ +- ath10k_wmi_tx_beacons_nowait(ar); ++ } else { ++ /* try to send pending beacons first. they take priority */ ++ ath10k_wmi_tx_beacons_nowait(ar); + +- ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); +- +- if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) +- ret = -ESHUTDOWN; ++ ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + ++ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) ++ ret = -ESHUTDOWN; ++ } + (ret != -EAGAIN); + }), 3 * HZ); + +-- +2.53.0 + diff --git a/queue-6.18/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch b/queue-6.18/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch new file mode 100644 index 0000000000..64a7163c14 --- /dev/null +++ b/queue-6.18/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch @@ -0,0 +1,39 @@ +From 417cac44661f42049f4d89f43d1cb1d59f08a1d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:40 +0200 +Subject: wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm() + +From: Nicolas Escande + +[ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ] + +This is similar to what was fixed by previous patches. We have a call +to ath11k_wmi_cmd_send() which does check the return value, but forgot +to free the related skb on error. + +Fixes: b43310e44edc ("wifi: ath11k: factory test mode support") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/testmode.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c +index a9751ea2a0b73..c72eed358f6dd 100644 +--- a/drivers/net/wireless/ath/ath11k/testmode.c ++++ b/drivers/net/wireless/ath/ath11k/testmode.c +@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret); ++ dev_kfree_skb(skb); + goto out; + } + +-- +2.53.0 + diff --git a/queue-6.18/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-6.18/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..302b9ac7bd --- /dev/null +++ b/queue-6.18/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,77 @@ +From 5ad07aca162adb7e0eaa873a12ee2fde602bf7df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 110035dae8a61..e1b00dc811e7b 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -9191,6 +9191,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9204,14 +9205,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9226,7 +9233,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +-- +2.53.0 + diff --git a/queue-6.18/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch b/queue-6.18/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch new file mode 100644 index 0000000000..50745ff94c --- /dev/null +++ b/queue-6.18/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch @@ -0,0 +1,73 @@ +From 1dc438f43879a2ec921cf7ef84f34f2b858c8e56 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:50:35 +0100 +Subject: wifi: ath11k: fix peer resolution on rx path when peer_id=0 + +From: Matthew Leach + +[ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] + +It has been observed that on certain chipsets a peer can be assigned +peer_id=0. For reception of non-aggregated MPDUs this is fine as +ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer +based upon the source MAC address. On an aggregated link, the mpdu_start +header is only populated by hardware on the first sub-MSDU. This causes +the peer resolution to be skipped for the subsequent MSDUs and the +encryption type of these frames to be set to an incorrect value, +resulting in these MSDUs being dropped by ieee80211. + +ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 +ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 + +Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and +ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID +of 0 to be resolved. + +Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 + +Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") +Reviewed-by: Baochen Qiang +Signed-off-by: Matthew Leach +Reviewed-by: P Praneesh +Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- + drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index 44eea682c297b..5666f66474455 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2214,8 +2214,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) + + lockdep_assert_held(&ab->base_lock); + +- if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + + if (peer) + return peer; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 753bd93f02123..51e0840bc0d1e 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; +- u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); +- if (peer_id) +- ppdu_info->peer_id = peer_id; ++ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +-- +2.53.0 + diff --git a/queue-6.18/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch b/queue-6.18/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch new file mode 100644 index 0000000000..6e7692a50f --- /dev/null +++ b/queue-6.18/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch @@ -0,0 +1,71 @@ +From 068835f726aefbf3dc3acff6c65d6f0658d8ded1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:14:56 +0300 +Subject: wifi: iwlwifi: mld: don't dereference a pointer before NULL checking + it + +From: Miri Korenblit + +[ Upstream commit d733ed481fd20a8e7bfe5119c4e77761ba3f87ee ] + +In iwl_mld_remove_link, the link->fw_id is saved at the beginning of the +function so we have it after we freed the link. + +But the link pointer can be NULL, and is not checked when the fw_id is +stored. + +Fix it by simply freeing the link at the end of the function. + +fFixes: 0e66a39f4f0e ("wifi: iwlwifi: fix potential use after free in iwl_mld_remove_link()") +Reviewed-by: Johannes Berg +Link: https://patch.msgid.link/20260515151351.371f40fc6711.I6a82cfe9655564e9c5731af91c36493b26b1208e@changeid +Signed-off-by: Miri Korenblit +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mld/link.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c +index f6f52d297a72c..e67ba3a24d025 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c ++++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + /* +- * Copyright (C) 2024-2025 Intel Corporation ++ * Copyright (C) 2024-2026 Intel Corporation + */ + + #include "constants.h" +@@ -501,7 +501,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = link == &mld_vif->deflink; +- u8 fw_id = link->fw_id; + + if (WARN_ON(!link || link->active)) + return; +@@ -509,15 +508,15 @@ void iwl_mld_remove_link(struct iwl_mld *mld, + iwl_mld_rm_link_from_fw(mld, bss_conf); + /* Continue cleanup on failure */ + +- if (!is_deflink) +- kfree_rcu(link, rcu_head); +- + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + +- if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links)) ++ if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) + return; + +- RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL); ++ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); ++ ++ if (!is_deflink) ++ kfree_rcu(link, rcu_head); + } + + void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, +-- +2.53.0 + diff --git a/queue-6.18/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch b/queue-6.18/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch new file mode 100644 index 0000000000..5022ceaf27 --- /dev/null +++ b/queue-6.18/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch @@ -0,0 +1,76 @@ +From 1cf3c8aca145e95c2e5b485cedf76a11afa10036 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 4 Apr 2026 22:41:44 -0700 +Subject: wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is + disabled + +From: Cole Leavitt + +[ Upstream commit 92cee08dc4f00e77fd1317e4343c5d458b0abab7 ] + +When the TLC notification disables AMSDU for a TID, the MLD driver sets +max_tid_amsdu_len to the sentinel value 1. The TSO segmentation path in +iwl_mld_tx_tso_segment() checks for zero but not for this sentinel, +allowing it to reach the num_subframes calculation: + + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad) + = (1 + 2) / (1534 + 2) = 0 + +This zero propagates to iwl_tx_tso_segment() which sets: + + gso_size = num_subframes * mss = 0 + +Calling skb_gso_segment() with gso_size=0 creates over 32000 tiny +segments from a single GSO skb. This floods the TX ring with ~1024 +micro-frames (the rest are purged), creating a massive burst of TX +completion events that can lead to memory corruption and a subsequent +use-after-free in TCP's retransmit queue (refcount underflow in +tcp_shifted_skb, NULL deref in tcp_rack_detect_loss). + +The MVM driver is immune because it checks mvmsta->amsdu_enabled before +reaching the num_subframes calculation. The MLD driver has no equivalent +bitmap check and relies solely on max_tid_amsdu_len, which does not +catch the sentinel value. + +Fix this by detecting the sentinel value (max_tid_amsdu_len == 1) at the +existing check and falling back to non-AMSDU TSO segmentation. Also add +a WARN_ON_ONCE guard after the num_subframes division as defense-in-depth +to catch any future code paths that produce zero through a different +mechanism. + +Suggested-by: Miriam Rachel Korenblit +Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") +Signed-off-by: Cole Leavitt +Link: https://patch.msgid.link/20260405054145.1064152-3-cole@unwrap.rs +Signed-off-by: Miri Korenblit +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mld/tx.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c +index c54ea3a91b667..a60bfb1a2ab22 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c ++++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c +@@ -828,7 +828,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + return -EINVAL; + + max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid]; +- if (!max_tid_amsdu_len) ++ if (!max_tid_amsdu_len || max_tid_amsdu_len == 1) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ +@@ -840,6 +840,9 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + */ + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad); + ++ if (WARN_ON_ONCE(!num_subframes)) ++ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); ++ + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; +-- +2.53.0 + diff --git a/queue-6.18/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch b/queue-6.18/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch new file mode 100644 index 0000000000..232e4eeaef --- /dev/null +++ b/queue-6.18/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch @@ -0,0 +1,55 @@ +From d8122bf9e6a888ba3432e3000bfc4e3eecbd1469 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 12:29:08 +0200 +Subject: wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs + +From: Alexandru Hossu + +[ Upstream commit f718506edd2d9c6a308ded9d13c632bf7b7d5a2c ] + +IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID is 0x000f, so link_id extracted +from a PRIO_ACCESS ML element PER_STA_PROFILE subelement can be 0..15. +sdata->link[] has IEEE80211_MLD_MAX_NUM_LINKS (15) entries (indices 0..14), +making index 15 out-of-bounds. + +A connected WiFi 7 AP can trigger this by sending an EPCS Enable Response +action frame with a PER_STA_PROFILE subelement where link_id = 15. The +unsolicited-notification path (dialog_token = 0) is reachable any time +EPCS is already enabled, without any prior client request. + +sdata->link[15] reads into the first word of sdata->activate_links_work +(a wiphy_work whose embedded list_head is non-NULL after INIT_LIST_HEAD), +so the NULL check on the result does not catch the invalid access. The +garbage pointer is then passed to ieee80211_sta_wmm_params(), which +dereferences link->sdata and crashes the kernel. + +The same class of bug was fixed for ieee80211_ml_reconfiguration() by +commit 162d331d833d ("wifi: mac80211: bounds-check link_id in +ieee80211_ml_reconfiguration"). + +Fixes: de86c5f60839 ("wifi: mac80211: Add support for EPCS configuration") +Signed-off-by: Alexandru Hossu +Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/mlme.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c +index ce247dde048c0..5d1da779cd6f2 100644 +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -10997,6 +10997,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, + control = get_unaligned_le16(pos); + link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID; + ++ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) ++ continue; ++ + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; +-- +2.53.0 + diff --git a/queue-6.18/wifi-mac80211-fix-mle-defragmentation.patch b/queue-6.18/wifi-mac80211-fix-mle-defragmentation.patch new file mode 100644 index 0000000000..c3ab44aed4 --- /dev/null +++ b/queue-6.18/wifi-mac80211-fix-mle-defragmentation.patch @@ -0,0 +1,160 @@ +From 8e847ddfb68139e9decf9779a963a94505234c47 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:10:31 +0200 +Subject: wifi: mac80211: fix MLE defragmentation + +From: Johannes Berg + +[ Upstream commit a74e893f30db64cdce0fc7a96d3baa417bcd55f5 ] + +If either reconf or EPCS multi-link element (MLE) is contained in +a non-transmitted profile, the defragmentation routine is called +with a pointer to the defragmented copy, but the original elements. + +This is incorrect for two reasons: + - if the original defragmentation was needed, it will not find the + correct data + - if the original frame is at a higher address, the parsing will + potentially overrun the heap data (though given the layout of + the buffers, only into the new defragmentation buffer, and then + it has to stop and fail once that's filled with copied data. + +Fix it by tracking the container along with the pointer and in +doing so also unify the two almost identical defragmentation +routines. + +Fixes: 4d70e9c5488d ("wifi: mac80211: defragment reconfiguration MLE when parsing") +Reviewed-by: Miriam Rachel Korenblit +Reviewed-by: Ilan Peer +Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/parse.c | 71 +++++++++++++++++++------------------------- + 1 file changed, 31 insertions(+), 40 deletions(-) + +diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c +index c5e0f7f460048..b9ec99f51851a 100644 +--- a/net/mac80211/parse.c ++++ b/net/mac80211/parse.c +@@ -34,6 +34,13 @@ + #include "led.h" + #include "wep.h" + ++struct ieee80211_elem_defrag { ++ const struct element *elem; ++ /* container start/len */ ++ const u8 *start; ++ size_t len; ++}; ++ + struct ieee80211_elems_parse { + /* must be first for kfree to work */ + struct ieee802_11_elems elems; +@@ -41,11 +48,7 @@ struct ieee80211_elems_parse { + /* The basic Multi-Link element in the original elements */ + const struct element *ml_basic_elem; + +- /* The reconfiguration Multi-Link element in the original elements */ +- const struct element *ml_reconf_elem; +- +- /* The EPCS Multi-Link element in the original elements */ +- const struct element *ml_epcs_elem; ++ struct ieee80211_elem_defrag ml_reconf, ml_epcs; + + bool multi_link_inner; + bool skip_vendor; +@@ -162,10 +165,14 @@ ieee80211_parse_extension_element(u32 *crc, + } + break; + case IEEE80211_ML_CONTROL_TYPE_RECONF: +- elems_parse->ml_reconf_elem = elem; ++ elems_parse->ml_reconf.elem = elem; ++ elems_parse->ml_reconf.start = params->start; ++ elems_parse->ml_reconf.len = params->len; + break; + case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: +- elems_parse->ml_epcs_elem = elem; ++ elems_parse->ml_epcs.elem = elem; ++ elems_parse->ml_epcs.start = params->start; ++ elems_parse->ml_epcs.len = params->len; + break; + default: + break; +@@ -950,46 +957,27 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, + sub->start, sub->len); + } + +-static void +-ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse) +-{ +- struct ieee802_11_elems *elems = &elems_parse->elems; +- ssize_t ml_len; +- +- ml_len = cfg80211_defragment_element(elems_parse->ml_reconf_elem, +- elems->ie_start, +- elems->total_len, +- elems_parse->scratch_pos, +- elems_parse->scratch + +- elems_parse->scratch_len - +- elems_parse->scratch_pos, +- WLAN_EID_FRAGMENT); +- if (ml_len < 0) +- return; +- elems->ml_reconf = (void *)elems_parse->scratch_pos; +- elems->ml_reconf_len = ml_len; +- elems_parse->scratch_pos += ml_len; +-} +- +-static void +-ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse) ++static const void * ++ieee80211_mle_defrag(struct ieee80211_elems_parse *elems_parse, ++ struct ieee80211_elem_defrag *defrag, ++ size_t *out_len) + { +- struct ieee802_11_elems *elems = &elems_parse->elems; ++ const void *ret; + ssize_t ml_len; + +- ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem, +- elems->ie_start, +- elems->total_len, ++ ml_len = cfg80211_defragment_element(defrag->elem, ++ defrag->start, defrag->len, + elems_parse->scratch_pos, + elems_parse->scratch + + elems_parse->scratch_len - + elems_parse->scratch_pos, + WLAN_EID_FRAGMENT); + if (ml_len < 0) +- return; +- elems->ml_epcs = (void *)elems_parse->scratch_pos; +- elems->ml_epcs_len = ml_len; ++ return NULL; ++ ret = elems_parse->scratch_pos; ++ *out_len = ml_len; + elems_parse->scratch_pos += ml_len; ++ return ret; + } + + struct ieee802_11_elems * +@@ -1069,9 +1057,12 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); + } + +- ieee80211_mle_defrag_reconf(elems_parse); +- +- ieee80211_mle_defrag_epcs(elems_parse); ++ elems->ml_reconf = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_reconf, ++ &elems->ml_reconf_len); ++ elems->ml_epcs = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_epcs, ++ &elems->ml_epcs_len); + + if (elems->tim && !elems->parse_error) { + const struct ieee80211_tim_ie *tim_ie = elems->tim; +-- +2.53.0 + diff --git a/queue-6.18/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch b/queue-6.18/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch new file mode 100644 index 0000000000..3f8a442ac6 --- /dev/null +++ b/queue-6.18/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch @@ -0,0 +1,51 @@ +From a823f8f1c5e8e4d4d5f8b0b8315cdadaa00c979e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:57:32 +0530 +Subject: wifi: wilc1000: fix dma_buffer leak on bus acquire failure + +From: Shitalkumar Gandhi + +[ Upstream commit dd7b6a8671939708cc4b7a46786d8c11297e8f69 ] + +wilc_wlan_firmware_download() allocates dma_buffer with kmalloc() at +the top of the function and uses a 'fail:' label to free it via +kfree(dma_buffer) on error. + +All later error paths correctly use 'goto fail' to route through this +cleanup. However, the early failure path after the first acquire_bus() +call uses a bare 'return ret;', which leaks dma_buffer whenever the bus +acquire fails. + +Replace the early return with goto fail so the existing cleanup path +runs. + +Found via a custom Coccinelle semantic patch hunting for kmalloc'd +locals leaked on early-return error paths in driver firmware-download +code. + +Fixes: 1241c5650ff7 ("wifi: wilc1000: Fill in missing error handling") +Signed-off-by: Shitalkumar Gandhi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260511042732.998311-1-shitalkumar.gandhi@cambiumnetworks.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/microchip/wilc1000/wlan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c +index fedc7d59216ad..1cc4ee62987d4 100644 +--- a/drivers/net/wireless/microchip/wilc1000/wlan.c ++++ b/drivers/net/wireless/microchip/wilc1000/wlan.c +@@ -1265,7 +1265,7 @@ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, + + ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); + if (ret) +- return ret; ++ goto fail; + + wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); + reg &= ~BIT(10); +-- +2.53.0 + diff --git a/queue-6.18/x86-mce-restore-mca-polling-interval-halving.patch b/queue-6.18/x86-mce-restore-mca-polling-interval-halving.patch new file mode 100644 index 0000000000..6cd52888a1 --- /dev/null +++ b/queue-6.18/x86-mce-restore-mca-polling-interval-halving.patch @@ -0,0 +1,138 @@ +From 58bc96e02ee0797ee3c6092a3adf7d500abe68fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 16 Mar 2026 16:12:00 +0100 +Subject: x86/mce: Restore MCA polling interval halving + +From: Borislav Petkov (AMD) + +[ Upstream commit ea324444ece9f301b5c4ff71b258cc68990c4d61 ] + +RongQing reported that the MCA polling interval doesn't halve when an +error gets logged. It was traced down to the commit in Fixes:, because: + + mce_timer_fn() + |-> mce_poll_banks() + |-> machine_check_poll() + |-> mce_log() + +which will queue the work and return. + +Now, back in mce_timer_fn(): + + /* + * Alert userspace if needed. If we logged an MCE, reduce the polling + * interval, otherwise increase the polling interval. + */ + if (mce_notify_irq()) + +<--- here we haven't ran the notifier chain yet so mce_need_notify is +not set yet so this won't hit and we won't halve the interval iv. + +Now the notifier chain runs. mce_early_notifier() sets the bit, does +mce_notify_irq(), that clears the bit and then the notifier chain +a little later logs the error. + +So this is a silly timing issue. + +But, that's all unnecessary. + +All it needs to happen here is, the "should we notify of a logged MCE" +mce_notify_irq() asks, should be simply a question to the mce gen pool: +"Are you empty?" + +And that then turns into a simple yes or no answer and it all +JustWorks(tm). + +So do that and also distribute the functionality where it belongs: + - Print that MCE events have been logged in mce_log() + - Trigger the mcelog tool specific work in the first notifier + +As a result, mce_notify_irq() can go now. + +Fixes: 011d82611172 ("RAS: Add a Corrected Errors Collector") +Reported-by: Li RongQing +Signed-off-by: Borislav Petkov (AMD) +Reviewed-by: Qiuxu Zhuo +Tested-by: Qiuxu Zhuo +Link: https://lore.kernel.org/r/20260112082747.2842-1-lirongqing@baidu.com +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/cpu/mce/core.c | 33 +++++---------------------------- + 1 file changed, 5 insertions(+), 28 deletions(-) + +diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c +index 460e90a1a0b17..c8b112c6c5492 100644 +--- a/arch/x86/kernel/cpu/mce/core.c ++++ b/arch/x86/kernel/cpu/mce/core.c +@@ -89,7 +89,6 @@ struct mca_config mca_cfg __read_mostly = { + }; + + static DEFINE_PER_CPU(struct mce_hw_err, hw_errs_seen); +-static unsigned long mce_need_notify; + + /* + * MCA banks polled by the period polling timer for corrected events. +@@ -151,8 +150,10 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm); + + void mce_log(struct mce_hw_err *err) + { +- if (mce_gen_pool_add(err)) ++ if (mce_gen_pool_add(err)) { ++ pr_info(HW_ERR "Machine check events logged\n"); + irq_work_queue(&mce_irq_work); ++ } + } + EXPORT_SYMBOL_GPL(mce_log); + +@@ -584,28 +585,6 @@ bool mce_is_correctable(struct mce *m) + } + EXPORT_SYMBOL_GPL(mce_is_correctable); + +-/* +- * Notify the user(s) about new machine check events. +- * Can be called from interrupt context, but not from machine check/NMI +- * context. +- */ +-static bool mce_notify_irq(void) +-{ +- /* Not more than two messages every minute */ +- static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); +- +- if (test_and_clear_bit(0, &mce_need_notify)) { +- mce_work_trigger(); +- +- if (__ratelimit(&ratelimit)) +- pr_info(HW_ERR "Machine check events logged\n"); +- +- return true; +- } +- +- return false; +-} +- + static int mce_early_notifier(struct notifier_block *nb, unsigned long val, + void *data) + { +@@ -617,9 +596,7 @@ static int mce_early_notifier(struct notifier_block *nb, unsigned long val, + /* Emit the trace record: */ + trace_mce_record(err); + +- set_bit(0, &mce_need_notify); +- +- mce_notify_irq(); ++ mce_work_trigger(); + + return NOTIFY_DONE; + } +@@ -1771,7 +1748,7 @@ static void mce_timer_fn(struct timer_list *t) + * Alert userspace if needed. If we logged an MCE, reduce the polling + * interval, otherwise increase the polling interval. + */ +- if (mce_notify_irq()) ++ if (!mce_gen_pool_empty()) + iv = max(iv / 2, (unsigned long) HZ/100); + else + iv = min(iv * 2, round_jiffies_relative(check_interval * HZ)); +-- +2.53.0 + diff --git a/queue-6.18/x86-xen-fix-xen_e820_swap_entry_with_ram.patch b/queue-6.18/x86-xen-fix-xen_e820_swap_entry_with_ram.patch new file mode 100644 index 0000000000..40e717d6ce --- /dev/null +++ b/queue-6.18/x86-xen-fix-xen_e820_swap_entry_with_ram.patch @@ -0,0 +1,39 @@ +From ad710c5c32984d38b795f28569186bb529a740a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 12:24:17 +0200 +Subject: x86/xen: Fix xen_e820_swap_entry_with_ram() + +From: Juergen Gross + +[ Upstream commit 28e03f78e69cf6628b81f24777799778528a84c1 ] + +When swapping a not page-aligned E820 map entry with RAM, the start +address of the modified entry is calculated wrong (the offset into the +page is subtracted instead of being added to the page address). + +Fixes: be35d91c8880 ("xen: tolerate ACPI NVS memory overlapping with Xen allocated memory") +Reported-by: Jan Beulich +Reviewed-by: Jan Beulich +Signed-off-by: Juergen Gross +Message-ID: <20260505102417.208138-1-jgross@suse.com> +Signed-off-by: Sasha Levin +--- + arch/x86/xen/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c +index 3823e52aef523..6260f65a78c5e 100644 +--- a/arch/x86/xen/setup.c ++++ b/arch/x86/xen/setup.c +@@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) + /* Fill new entry (keep size and page offset). */ + entry->type = swap_entry->type; + entry->addr = entry_end - swap_size + +- swap_addr - swap_entry->addr; ++ swap_entry->addr - swap_addr; + entry->size = swap_entry->size; + + /* Convert old entry to RAM, align to pages. */ +-- +2.53.0 + diff --git a/queue-6.18/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch b/queue-6.18/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch new file mode 100644 index 0000000000..a43995db42 --- /dev/null +++ b/queue-6.18/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch @@ -0,0 +1,51 @@ +From 119ab2d36e04f95aa06691b3da115cdd53eaf800 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 22:58:15 +0200 +Subject: zonefs: handle integer overflow in zonefs_fname_to_fno + +From: Johannes Thumshirn + +[ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ] + +In zonefs the file name in one of the two directories corresponds to the +zone number. + +Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), +where the parsing of the zone number from the file name can overflow the +'long' data type. + +Add a check for integer overflows and if the fno 'long' did overflow +return -ENOENT. + +Reported-by: Alexey Dobriyan +Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed") +Signed-off-by: Johannes Thumshirn +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + fs/zonefs/super.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c +index 70be0b3dda496..7e345a6aa5510 100644 +--- a/fs/zonefs/super.c ++++ b/fs/zonefs/super.c +@@ -610,10 +610,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname) + return c - '0'; + + for (i = 0, rname = name + len - 1; i < len; i++, rname--) { ++ long digit; ++ + c = *rname; + if (!isdigit(c)) + return -ENOENT; +- fno += (c - '0') * shift; ++ digit = (c - '0') * shift; ++ if (check_add_overflow(fno, digit, &fno)) ++ return -ENOENT; + shift *= 10; + } + +-- +2.53.0 + diff --git a/queue-6.6/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch b/queue-6.6/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch new file mode 100644 index 0000000000..1d45ba57bb --- /dev/null +++ b/queue-6.6/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch @@ -0,0 +1,78 @@ +From b347f48fa72972d3f9e72c5012b6159e3e742569 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:39:01 -0700 +Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap + +From: Zack McKevitt + +[ Upstream commit aa16b2bc0f02709919e2435f531406531e5bcc69 ] + +The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to +(re)mapping beyond the VMA if the BO is too large. This can cause use +after free issues when munmap() unmaps only the VMA region and not the +additional mappings. To prevent this, check the remaining size of the +VMA before remapping and truncate the remapped length if sg->length is +too large. + +Reported-by: Lukas Maar +Fixes: ff13be830333 ("accel/qaic: Add datapath") +Reviewed-by: Karol Wachowski +Signed-off-by: Zack McKevitt +Reviewed-by: Jeff Hugo +[jhugo: fix braces from checkpatch --strict] +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_data.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c +index d00068987d9bd..7beab1309e369 100644 +--- a/drivers/accel/qaic/qaic_data.c ++++ b/drivers/accel/qaic/qaic_data.c +@@ -595,8 +595,11 @@ static const struct vm_operations_struct drm_vm_ops = { + static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) + { + struct qaic_bo *bo = to_qaic_bo(obj); ++ unsigned long remap_start; + unsigned long offset = 0; ++ unsigned long remap_end; + struct scatterlist *sg; ++ unsigned long length; + int ret = 0; + + if (obj->import_attach) +@@ -604,11 +607,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc + + for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { + if (sg_page(sg)) { ++ /* if sg is too large for the VMA, so truncate it to fit */ ++ if (check_add_overflow(vma->vm_start, offset, &remap_start)) ++ return -EINVAL; ++ if (check_add_overflow(remap_start, sg->length, &remap_end)) ++ return -EINVAL; ++ ++ if (remap_end > vma->vm_end) { ++ if (check_sub_overflow(vma->vm_end, remap_start, &length)) ++ return -EINVAL; ++ } else { ++ length = sg->length; ++ } ++ ++ if (length == 0) ++ goto out; ++ + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), +- sg->length, vma->vm_page_prot); ++ length, vma->vm_page_prot); + if (ret) + goto out; +- offset += sg->length; ++ offset += length; + } + } + +-- +2.53.0 + diff --git a/queue-6.6/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch b/queue-6.6/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch new file mode 100644 index 0000000000..5eaa8aa46c --- /dev/null +++ b/queue-6.6/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch @@ -0,0 +1,44 @@ +From 205e99bda6b8d25705601523784bee2c604acb2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:01:39 +0800 +Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion + +From: Shuhao Fu + +[ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers are expected to balance it with acpi_dev_put(). + +When no companion is already attached, cs35l56_hda_read_acpi() looks +up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves +the lookup reference held. + +ACPI_COMPANION_SET() does not take ownership of that reference, so +drop it with acpi_dev_put() after attaching the companion. + +Fixes: 73cfbfa9caea ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/pci/hda/cs35l56_hda.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c +index bae7b1d592c6c..afb397c32361f 100644 +--- a/sound/pci/hda/cs35l56_hda.c ++++ b/sound/pci/hda/cs35l56_hda.c +@@ -855,6 +855,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) + return -ENODEV; + } + ACPI_COMPANION_SET(cs35l56->base.dev, adev); ++ acpi_dev_put(adev); + } + + property = "cirrus,dev-index"; +-- +2.53.0 + diff --git a/queue-6.6/alsa-seq-serialize-ump-output-teardown-with-event_in.patch b/queue-6.6/alsa-seq-serialize-ump-output-teardown-with-event_in.patch new file mode 100644 index 0000000000..243b1f07cb --- /dev/null +++ b/queue-6.6/alsa-seq-serialize-ump-output-teardown-with-event_in.patch @@ -0,0 +1,174 @@ +From 0a94426882271a415f88a084a8fa92738d4ebb02 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:32:49 +0800 +Subject: ALSA: seq: Serialize UMP output teardown with event_input + +From: Zhang Cen + +[ Upstream commit 60a1969fae6209644698fca91c185d153674f631 ] + +seq_ump_process_event() borrows client->out_rfile.output without +synchronizing with the first-open and last-close transition in +seq_ump_client_open() and seq_ump_client_close(). + +The last output unuse can therefore drop opened[STR_OUT] to zero and +release the rawmidi file while an in-flight event_input callback is still +inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream +runtime exposed to teardown before the write path has taken its own +buffer reference. + +Add a per-client rwlock for the event_input-visible output file. Publish +a newly opened output file under the write side, and hold the read side +from the output lookup through snd_rawmidi_kernel_write(). The last +output close copies and clears the visible output file under the write +side, then drops the lock and releases the saved rawmidi file. Use +IRQ-safe rwlock guards because event_input can also be reached from +atomic sequencer delivery. + +The buggy scenario involves two paths, with each column showing the +order within that path: + +path A label: event_input path path B label: last unuse path +1. seq_ump_process_event() reads 1. seq_ump_client_close() + client->out_rfile.output. drops opened[STR_OUT] to zero. +2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release() + has not yet pinned runtime. closes the output file. +3. The writer continues using 3. close_substream() frees + the borrowed substream. substream->runtime. + +This keeps the output substream and runtime alive for the full +event_input write while keeping rawmidi release outside the rwlock. + +KASAN reproduced this as a slab-use-after-free in +snd_rawmidi_kernel_write1(), with allocation through +seq_ump_use()/snd_seq_port_connect() and free through +seq_ump_unuse()/snd_seq_port_disconnect(). + +Suggested-by: Takashi Iwai + +Validation reproduced this kernel report: +KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400 +RIP: 0033:0x7f5528af837f +Read of size 8 +Call trace: + dump_stack_lvl+0x73/0xb0 (?:?) + print_report+0xd1/0x650 (?:?) + srso_alias_return_thunk+0x5/0xfbef5 (?:?) + __virt_addr_valid+0x1a7/0x340 (?:?) + kasan_complete_mode_report_info+0x64/0x200 (?:?) + kasan_report+0xf7/0x130 (?:?) + snd_rawmidi_kernel_write1+0x9d/0x400 (?:?) + __asan_load8+0x82/0xb0 (?:?) + update_stack_state+0x1ef/0x2d0 (?:?) + snd_rawmidi_kernel_write+0x1a/0x20 (?:?) + seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82) + __snd_seq_deliver_single_event+0x8a/0xe0 (?:?) + snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?) + lock_acquire+0x14e/0x2e0 (?:?) + find_held_lock+0x31/0x90 (?:?) + snd_seq_port_use_ptr+0xa6/0xe0 (?:?) + __kasan_check_write+0x18/0x20 (?:?) + do_raw_read_unlock+0x32/0xa0 (?:?) + _raw_read_unlock+0x26/0x50 (?:?) + snd_seq_deliver_single_event+0x45c/0x4b0 (?:?) + snd_seq_deliver_event+0x10d/0x1b0 (?:?) + snd_seq_client_enqueue_event+0x192/0x240 (?:?) + snd_seq_write+0x2cd/0x450 (?:?) + apparmor_file_permission+0x20/0x30 (?:?) + security_file_permission+0x51/0x60 (?:?) + vfs_write+0x1ce/0x850 (?:?) + __fget_files+0x12b/0x220 (?:?) + lock_release+0xc8/0x2a0 (?:?) + __rcu_read_unlock+0x74/0x2d0 (?:?) + __fget_files+0x135/0x220 (?:?) + ksys_write+0x15a/0x180 (?:?) + rcu_is_watching+0x24/0x60 (?:?) + __x64_sys_write+0x46/0x60 (?:?) + x64_sys_call+0x7d/0x20d0 (?:?) + do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) + entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) + +Fixes: 81fd444aa371 ("ALSA: seq: Bind UMP device") +Signed-off-by: Zhang Cen +Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/core/seq/seq_ump_client.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c +index 389516f09f099..914837929686f 100644 +--- a/sound/core/seq/seq_ump_client.c ++++ b/sound/core/seq/seq_ump_client.c +@@ -37,6 +37,7 @@ struct seq_ump_client { + struct snd_ump_endpoint *ump; /* assigned endpoint */ + int seq_client; /* sequencer client id */ + int opened[2]; /* current opens for each direction */ ++ rwlock_t output_lock; /* protects out_rfile output access */ + struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + struct seq_ump_input_buffer input; /* input parser context */ + void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ +@@ -88,6 +89,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + unsigned char type; + int len; + ++ guard(read_lock_irqsave)(&client->output_lock); + substream = client->out_rfile.output; + if (!substream) + return -ENODEV; +@@ -106,6 +108,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + static int seq_ump_client_open(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + int err; + + guard(mutex)(&ump->open_mutex); +@@ -113,9 +116,11 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + err = snd_rawmidi_kernel_open(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, +- &client->out_rfile); ++ &rfile); + if (err < 0) + return err; ++ scoped_guard(write_lock_irqsave, &client->output_lock) ++ client->out_rfile = rfile; + } + client->opened[dir]++; + return 0; +@@ -125,11 +130,19 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + static int seq_ump_client_close(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + + guard(mutex)(&ump->open_mutex); +- if (!--client->opened[dir]) +- if (dir == STR_OUT) +- snd_rawmidi_kernel_release(&client->out_rfile); ++ if (!--client->opened[dir]) { ++ if (dir == STR_OUT) { ++ scoped_guard(write_lock_irqsave, &client->output_lock) { ++ rfile = client->out_rfile; ++ client->out_rfile = (struct snd_rawmidi_file){}; ++ } ++ if (rfile.rmidi) ++ snd_rawmidi_kernel_release(&rfile); ++ } ++ } + return 0; + } + +@@ -430,6 +443,7 @@ static int snd_seq_ump_probe(struct device *_dev) + + INIT_WORK(&client->group_notify_work, handle_group_notify); + client->ump = ump; ++ rwlock_init(&client->output_lock); + + client->seq_client = + snd_seq_create_kernel_client(card, ump->core.device, +-- +2.53.0 + diff --git a/queue-6.6/alsa-seq-ump-use-guard-for-locking.patch b/queue-6.6/alsa-seq-ump-use-guard-for-locking.patch new file mode 100644 index 0000000000..139f909cfd --- /dev/null +++ b/queue-6.6/alsa-seq-ump-use-guard-for-locking.patch @@ -0,0 +1,68 @@ +From 0b1af96c7c0e6da1468d713b09477d714e510510 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 27 Feb 2024 09:53:00 +0100 +Subject: ALSA: seq: ump: Use guard() for locking + +From: Takashi Iwai + +[ Upstream commit 6487e363714c28c4b62ac149e7d907cfeeedb3ad ] + +We can simplify the code gracefully with new guard() macro and co for +automatic cleanup of locks. + +Only the code refactoring, and no functional changes. + +Signed-off-by: Takashi Iwai +Link: https://lore.kernel.org/r/20240227085306.9764-19-tiwai@suse.de +Stable-dep-of: 60a1969fae62 ("ALSA: seq: Serialize UMP output teardown with event_input") +Signed-off-by: Sasha Levin +--- + sound/core/seq/seq_ump_client.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c +index 55923ee6c97ae..389516f09f099 100644 +--- a/sound/core/seq/seq_ump_client.c ++++ b/sound/core/seq/seq_ump_client.c +@@ -106,21 +106,19 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + static int seq_ump_client_open(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; +- int err = 0; ++ int err; + +- mutex_lock(&ump->open_mutex); ++ guard(mutex)(&ump->open_mutex); + if (dir == STR_OUT && !client->opened[dir]) { + err = snd_rawmidi_kernel_open(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, + &client->out_rfile); + if (err < 0) +- goto unlock; ++ return err; + } + client->opened[dir]++; +- unlock: +- mutex_unlock(&ump->open_mutex); +- return err; ++ return 0; + } + + /* close the rawmidi */ +@@ -128,11 +126,10 @@ static int seq_ump_client_close(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; + +- mutex_lock(&ump->open_mutex); ++ guard(mutex)(&ump->open_mutex); + if (!--client->opened[dir]) + if (dir == STR_OUT) + snd_rawmidi_kernel_release(&client->out_rfile); +- mutex_unlock(&ump->open_mutex); + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.6/arm-integrator-fix-early-initialization.patch b/queue-6.6/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..9a3d7d7478 --- /dev/null +++ b/queue-6.6/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From a297fb159304d21bf98c79b3e8956cafaaa19624 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c +index 2ed4ded56b3fe..03dfb5f720b7b 100644 +--- a/arch/arm/mach-versatile/integrator_cp.c ++++ b/arch/arm/mach-versatile/integrator_cp.c +@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-6.6/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch b/queue-6.6/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch new file mode 100644 index 0000000000..bf4f55a2d4 --- /dev/null +++ b/queue-6.6/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch @@ -0,0 +1,48 @@ +From a524e7f889c486b2e5d4a0888bd19fc0cb505e99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:30:57 +0100 +Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove() + +From: Richard Fitzgerald + +[ Upstream commit 18e7bd9f2446664053f8c34b72abd4606d22d858 ] + +Use flush_work() instead of cancel_work_sync() to terminate pending IRQ +work in cs35l56_sdw_remove(). And flush_work() again after masking the +interrupts to flush any queueing that was racing with the masking. This is +the same sequence as cs35l56_sdw_system_suspend(). + +cs35l56_sdw_interrupt() takes the pm_runtime to prevent the bus powering- +down before the interrupt status can be read and handled. The work releases +this pm_runtime. So cancelling it, instead of flushing, could leave an +unbalanced pm_runtime. + +Signed-off-by: Richard Fitzgerald +Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") +Link: https://patch.msgid.link/20260521123057.988732-1-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs35l56-sdw.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +index b433266b78446..7d893d1243a2a 100644 +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -524,10 +524,11 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; +- cancel_work_sync(&cs35l56->sdw_irq_work); ++ flush_work(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ flush_work(&cs35l56->sdw_irq_work); + + cs35l56_remove(cs35l56); + +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btmtk-add-the-function-to-get-the-fw-name.patch b/queue-6.6/bluetooth-btmtk-add-the-function-to-get-the-fw-name.patch new file mode 100644 index 0000000000..019ef2e3f6 --- /dev/null +++ b/queue-6.6/bluetooth-btmtk-add-the-function-to-get-the-fw-name.patch @@ -0,0 +1,77 @@ +From 0da75a1960341715c044aed69b0cf0fea70ea3ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 May 2024 16:15:17 -0700 +Subject: Bluetooth: btmtk: add the function to get the fw name + +From: Sean Wang + +[ Upstream commit 00f993fdec06c8f036a1b9c8ee6b004c17143bd1 ] + +Include a shared function to get the firmware name, to prevent repeating +code for similar chipsets. + +Signed-off-by: Sean Wang +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths") +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 18 ++++++++++++++++++ + drivers/bluetooth/btmtk.h | 8 ++++++++ + 2 files changed, 26 insertions(+) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index 4c53ab22d09b0..31fca4529b5ad 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -103,6 +103,24 @@ static void btmtk_coredump_notify(struct hci_dev *hdev, int state) + } + } + ++void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver, ++ u32 fw_flavor) ++{ ++ if (dev_id == 0x7925) ++ snprintf(buf, size, ++ "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin", ++ dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1); ++ else if (dev_id == 0x7961 && fw_flavor) ++ snprintf(buf, size, ++ "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin", ++ dev_id & 0xffff, (fw_ver & 0xff) + 1); ++ else ++ snprintf(buf, size, ++ "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin", ++ dev_id & 0xffff, (fw_ver & 0xff) + 1); ++} ++EXPORT_SYMBOL_GPL(btmtk_fw_get_filename); ++ + int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, + wmt_cmd_sync_func_t wmt_cmd_sync) + { +diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h +index cbcdb99a22e6d..e76b8a358be88 100644 +--- a/drivers/bluetooth/btmtk.h ++++ b/drivers/bluetooth/btmtk.h +@@ -160,6 +160,9 @@ int btmtk_register_coredump(struct hci_dev *hdev, const char *name, + u32 fw_version); + + int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb); ++ ++void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver, ++ u32 fw_flavor); + #else + + static inline int btmtk_set_bdaddr(struct hci_dev *hdev, +@@ -194,4 +197,9 @@ static int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb) + { + return -EOPNOTSUPP; + } ++ ++static void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, ++ u32 fw_ver, u32 fw_flavor) ++{ ++} + #endif +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch b/queue-6.6/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch new file mode 100644 index 0000000000..502bdd5efe --- /dev/null +++ b/queue-6.6/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch @@ -0,0 +1,43 @@ +From 516c05d1903a33ab6b5970d4977cc3c1d72afe0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:24:02 +0800 +Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths + +From: Jiajia Liu + +[ Upstream commit dd1dda6b8d6e1f4376a5b3055a04f0ecbdb4d6bd ] + +The setup_packet of control urb is not freed if usb_submit_urb fails or +the submitted urb is killed. Add free in these two paths. + +Fixes: a1c49c434e150 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") +Signed-off-by: Jiajia Liu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index 17cb58dce8140..ad8753dda826b 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -490,6 +490,7 @@ static void btmtk_usb_wmt_recv(struct urb *urb) + return; + } else if (urb->status == -ENOENT) { + /* Avoid suspend failed when usb_kill_urb */ ++ kfree(urb->setup_packet); + return; + } + +@@ -563,6 +564,7 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); ++ kfree(dr); + usb_unanchor_urb(urb); + } + +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btmtk-move-btusb_mtk_hci_wmt_sync-to-btmtk.patch b/queue-6.6/bluetooth-btmtk-move-btusb_mtk_hci_wmt_sync-to-btmtk.patch new file mode 100644 index 0000000000..87b5746d0f --- /dev/null +++ b/queue-6.6/bluetooth-btmtk-move-btusb_mtk_hci_wmt_sync-to-btmtk.patch @@ -0,0 +1,759 @@ +From ebf1e3642378fb0230660a9ac872af76096d9277 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jul 2024 14:01:13 +0800 +Subject: Bluetooth: btmtk: move btusb_mtk_hci_wmt_sync to btmtk.c + +From: Chris Lu + +[ Upstream commit d019930b0049fc2648a6b279893d8ad330596e81 ] + +Move btusb_mtk_hci_wmt_sync from btusb.c to btmtk.c which holds +vendor specific stuff and would make btusb.c clean. + +Add usb.h header to btmtksdio.c/btmtkuart.c for usb related element +defined in btmtk.h + +Signed-off-by: Sean Wang +Signed-off-by: Chris Lu +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths") +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 265 +++++++++++++++++++++++++++++++ + drivers/bluetooth/btmtk.h | 31 ++++ + drivers/bluetooth/btmtksdio.c | 1 + + drivers/bluetooth/btmtkuart.c | 1 + + drivers/bluetooth/btusb.c | 290 +--------------------------------- + 5 files changed, 305 insertions(+), 283 deletions(-) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index 0290ba9ace9a9..17cb58dce8140 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -4,6 +4,7 @@ + */ + #include + #include ++#include + + #include + #include +@@ -435,6 +436,270 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb) + } + EXPORT_SYMBOL_GPL(btmtk_process_coredump); + ++static void btmtk_usb_wmt_recv(struct urb *urb) ++{ ++ struct hci_dev *hdev = urb->context; ++ struct btmtk_data *data = hci_get_priv(hdev); ++ struct sk_buff *skb; ++ int err; ++ ++ if (urb->status == 0 && urb->actual_length > 0) { ++ hdev->stat.byte_rx += urb->actual_length; ++ ++ /* WMT event shouldn't be fragmented and the size should be ++ * less than HCI_WMT_MAX_EVENT_SIZE. ++ */ ++ skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC); ++ if (!skb) { ++ hdev->stat.err_rx++; ++ kfree(urb->setup_packet); ++ return; ++ } ++ ++ hci_skb_pkt_type(skb) = HCI_EVENT_PKT; ++ skb_put_data(skb, urb->transfer_buffer, urb->actual_length); ++ ++ /* When someone waits for the WMT event, the skb is being cloned ++ * and being processed the events from there then. ++ */ ++ if (test_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags)) { ++ data->evt_skb = skb_clone(skb, GFP_ATOMIC); ++ if (!data->evt_skb) { ++ kfree_skb(skb); ++ kfree(urb->setup_packet); ++ return; ++ } ++ } ++ ++ err = hci_recv_frame(hdev, skb); ++ if (err < 0) { ++ kfree_skb(data->evt_skb); ++ data->evt_skb = NULL; ++ kfree(urb->setup_packet); ++ return; ++ } ++ ++ if (test_and_clear_bit(BTMTK_TX_WAIT_VND_EVT, ++ &data->flags)) { ++ /* Barrier to sync with other CPUs */ ++ smp_mb__after_atomic(); ++ wake_up_bit(&data->flags, ++ BTMTK_TX_WAIT_VND_EVT); ++ } ++ kfree(urb->setup_packet); ++ return; ++ } else if (urb->status == -ENOENT) { ++ /* Avoid suspend failed when usb_kill_urb */ ++ return; ++ } ++ ++ usb_mark_last_busy(data->udev); ++ ++ /* The URB complete handler is still called with urb->actual_length = 0 ++ * when the event is not available, so we should keep re-submitting ++ * URB until WMT event returns, Also, It's necessary to wait some time ++ * between the two consecutive control URBs to relax the target device ++ * to generate the event. Otherwise, the WMT event cannot return from ++ * the device successfully. ++ */ ++ udelay(500); ++ ++ usb_anchor_urb(urb, data->ctrl_anchor); ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err < 0) { ++ kfree(urb->setup_packet); ++ /* -EPERM: urb is being killed; ++ * -ENODEV: device got disconnected ++ */ ++ if (err != -EPERM && err != -ENODEV) ++ bt_dev_err(hdev, "urb %p failed to resubmit (%d)", ++ urb, -err); ++ usb_unanchor_urb(urb); ++ } ++} ++ ++static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) ++{ ++ struct btmtk_data *data = hci_get_priv(hdev); ++ struct usb_ctrlrequest *dr; ++ unsigned char *buf; ++ int err, size = 64; ++ unsigned int pipe; ++ struct urb *urb; ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) ++ return -ENOMEM; ++ ++ dr = kmalloc(sizeof(*dr), GFP_KERNEL); ++ if (!dr) { ++ usb_free_urb(urb); ++ return -ENOMEM; ++ } ++ ++ dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; ++ dr->bRequest = 1; ++ dr->wIndex = cpu_to_le16(0); ++ dr->wValue = cpu_to_le16(48); ++ dr->wLength = cpu_to_le16(size); ++ ++ buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) { ++ kfree(dr); ++ usb_free_urb(urb); ++ return -ENOMEM; ++ } ++ ++ pipe = usb_rcvctrlpipe(data->udev, 0); ++ ++ usb_fill_control_urb(urb, data->udev, pipe, (void *)dr, ++ buf, size, btmtk_usb_wmt_recv, hdev); ++ ++ urb->transfer_flags |= URB_FREE_BUFFER; ++ ++ usb_anchor_urb(urb, data->ctrl_anchor); ++ err = usb_submit_urb(urb, GFP_KERNEL); ++ if (err < 0) { ++ if (err != -EPERM && err != -ENODEV) ++ bt_dev_err(hdev, "urb %p submission failed (%d)", ++ urb, -err); ++ usb_unanchor_urb(urb); ++ } ++ ++ usb_free_urb(urb); ++ ++ return err; ++} ++ ++int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, ++ struct btmtk_hci_wmt_params *wmt_params) ++{ ++ struct btmtk_data *data = hci_get_priv(hdev); ++ struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; ++ u32 hlen, status = BTMTK_WMT_INVALID; ++ struct btmtk_hci_wmt_evt *wmt_evt; ++ struct btmtk_hci_wmt_cmd *wc; ++ struct btmtk_wmt_hdr *hdr; ++ int err; ++ ++ /* Send the WMT command and wait until the WMT event returns */ ++ hlen = sizeof(*hdr) + wmt_params->dlen; ++ if (hlen > 255) ++ return -EINVAL; ++ ++ wc = kzalloc(hlen, GFP_KERNEL); ++ if (!wc) ++ return -ENOMEM; ++ ++ hdr = &wc->hdr; ++ hdr->dir = 1; ++ hdr->op = wmt_params->op; ++ hdr->dlen = cpu_to_le16(wmt_params->dlen + 1); ++ hdr->flag = wmt_params->flag; ++ memcpy(wc->data, wmt_params->data, wmt_params->dlen); ++ ++ set_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags); ++ ++ /* WMT cmd/event doesn't follow up the generic HCI cmd/event handling, ++ * it needs constantly polling control pipe until the host received the ++ * WMT event, thus, we should require to specifically acquire PM counter ++ * on the USB to prevent the interface from entering auto suspended ++ * while WMT cmd/event in progress. ++ */ ++ err = usb_autopm_get_interface(data->intf); ++ if (err < 0) ++ goto err_free_wc; ++ ++ err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc); ++ ++ if (err < 0) { ++ clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags); ++ usb_autopm_put_interface(data->intf); ++ goto err_free_wc; ++ } ++ ++ /* Submit control IN URB on demand to process the WMT event */ ++ err = btmtk_usb_submit_wmt_recv_urb(hdev); ++ ++ usb_autopm_put_interface(data->intf); ++ ++ if (err < 0) ++ goto err_free_wc; ++ ++ /* The vendor specific WMT commands are all answered by a vendor ++ * specific event and will have the Command Status or Command ++ * Complete as with usual HCI command flow control. ++ * ++ * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT ++ * state to be cleared. The driver specific event receive routine ++ * will clear that state and with that indicate completion of the ++ * WMT command. ++ */ ++ err = wait_on_bit_timeout(&data->flags, BTMTK_TX_WAIT_VND_EVT, ++ TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); ++ if (err == -EINTR) { ++ bt_dev_err(hdev, "Execution of wmt command interrupted"); ++ clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags); ++ goto err_free_wc; ++ } ++ ++ if (err) { ++ bt_dev_err(hdev, "Execution of wmt command timed out"); ++ clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags); ++ err = -ETIMEDOUT; ++ goto err_free_wc; ++ } ++ ++ if (data->evt_skb == NULL) ++ goto err_free_wc; ++ ++ /* Parse and handle the return WMT event */ ++ wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data; ++ if (wmt_evt->whdr.op != hdr->op) { ++ bt_dev_err(hdev, "Wrong op received %d expected %d", ++ wmt_evt->whdr.op, hdr->op); ++ err = -EIO; ++ goto err_free_skb; ++ } ++ ++ switch (wmt_evt->whdr.op) { ++ case BTMTK_WMT_SEMAPHORE: ++ if (wmt_evt->whdr.flag == 2) ++ status = BTMTK_WMT_PATCH_UNDONE; ++ else ++ status = BTMTK_WMT_PATCH_DONE; ++ break; ++ case BTMTK_WMT_FUNC_CTRL: ++ wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; ++ if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) ++ status = BTMTK_WMT_ON_DONE; ++ else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420) ++ status = BTMTK_WMT_ON_PROGRESS; ++ else ++ status = BTMTK_WMT_ON_UNDONE; ++ break; ++ case BTMTK_WMT_PATCH_DWNLD: ++ if (wmt_evt->whdr.flag == 2) ++ status = BTMTK_WMT_PATCH_DONE; ++ else if (wmt_evt->whdr.flag == 1) ++ status = BTMTK_WMT_PATCH_PROGRESS; ++ else ++ status = BTMTK_WMT_PATCH_UNDONE; ++ break; ++ } ++ ++ if (wmt_params->status) ++ *wmt_params->status = status; ++ ++err_free_skb: ++ kfree_skb(data->evt_skb); ++ data->evt_skb = NULL; ++err_free_wc: ++ kfree(wc); ++ return err; ++} ++EXPORT_SYMBOL_GPL(btmtk_usb_hci_wmt_sync); ++ + MODULE_AUTHOR("Sean Wang "); + MODULE_AUTHOR("Mark Chen "); + MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION); +diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h +index dde6fbdeb2b3b..3055b9728ae25 100644 +--- a/drivers/bluetooth/btmtk.h ++++ b/drivers/bluetooth/btmtk.h +@@ -28,6 +28,18 @@ + #define MTK_COREDUMP_END_LEN (sizeof(MTK_COREDUMP_END)) + #define MTK_COREDUMP_NUM 255 + ++/* UHW CR mapping */ ++#define MTK_BT_MISC 0x70002510 ++#define MTK_BT_SUBSYS_RST 0x70002610 ++#define MTK_UDMA_INT_STA_BT 0x74000024 ++#define MTK_UDMA_INT_STA_BT1 0x74000308 ++#define MTK_BT_WDT_STATUS 0x740003A0 ++#define MTK_EP_RST_OPT 0x74011890 ++#define MTK_EP_RST_IN_OUT_OPT 0x00010001 ++#define MTK_BT_RST_DONE 0x00000100 ++#define MTK_BT_RESET_REG_CONNV3 0x70028610 ++#define MTK_BT_READ_DEV_ID 0x70010200 ++ + enum { + BTMTK_WMT_PATCH_DWNLD = 0x1, + BTMTK_WMT_TEST = 0x2, +@@ -126,6 +138,10 @@ struct btmtk_hci_wmt_params { + u32 *status; + }; + ++enum { ++ BTMTK_TX_WAIT_VND_EVT, ++}; ++ + typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *); + + struct btmtk_coredump_info { +@@ -136,9 +152,15 @@ struct btmtk_coredump_info { + }; + + struct btmtk_data { ++ unsigned long flags; + u32 dev_id; + btmtk_reset_sync_func_t reset_sync; + struct btmtk_coredump_info cd_info; ++ ++ struct usb_device *udev; ++ struct usb_interface *intf; ++ struct usb_anchor *ctrl_anchor; ++ struct sk_buff *evt_skb; + }; + + typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *, +@@ -163,6 +185,9 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb); + + void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver, + u32 fw_flavor); ++ ++int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, ++ struct btmtk_hci_wmt_params *wmt_params); + #else + + static inline int btmtk_set_bdaddr(struct hci_dev *hdev, +@@ -202,4 +227,10 @@ static void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, + u32 fw_ver, u32 fw_flavor) + { + } ++ ++static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, ++ struct btmtk_hci_wmt_params *wmt_params) ++{ ++ return -EOPNOTSUPP; ++} + #endif +diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c +index 97659b4792e69..e249aa7587833 100644 +--- a/drivers/bluetooth/btmtksdio.c ++++ b/drivers/bluetooth/btmtksdio.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + #include +diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c +index 203a000a84e34..9823c40ae2680 100644 +--- a/drivers/bluetooth/btmtkuart.c ++++ b/drivers/bluetooth/btmtkuart.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index 4413ff997aa08..3d0533a05134e 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -2686,282 +2686,6 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb) + return hci_recv_frame(hdev, skb); + } + +-/* UHW CR mapping */ +-#define MTK_BT_MISC 0x70002510 +-#define MTK_BT_SUBSYS_RST 0x70002610 +-#define MTK_UDMA_INT_STA_BT 0x74000024 +-#define MTK_UDMA_INT_STA_BT1 0x74000308 +-#define MTK_BT_WDT_STATUS 0x740003A0 +-#define MTK_EP_RST_OPT 0x74011890 +-#define MTK_EP_RST_IN_OUT_OPT 0x00010001 +-#define MTK_BT_RST_DONE 0x00000100 +-#define MTK_BT_RESET_REG_CONNV3 0x70028610 +-#define MTK_BT_READ_DEV_ID 0x70010200 +- +- +-static void btusb_mtk_wmt_recv(struct urb *urb) +-{ +- struct hci_dev *hdev = urb->context; +- struct btusb_data *data = hci_get_drvdata(hdev); +- struct sk_buff *skb; +- int err; +- +- if (urb->status == 0 && urb->actual_length > 0) { +- hdev->stat.byte_rx += urb->actual_length; +- +- /* WMT event shouldn't be fragmented and the size should be +- * less than HCI_WMT_MAX_EVENT_SIZE. +- */ +- skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC); +- if (!skb) { +- hdev->stat.err_rx++; +- kfree(urb->setup_packet); +- return; +- } +- +- hci_skb_pkt_type(skb) = HCI_EVENT_PKT; +- skb_put_data(skb, urb->transfer_buffer, urb->actual_length); +- +- /* When someone waits for the WMT event, the skb is being cloned +- * and being processed the events from there then. +- */ +- if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) { +- data->evt_skb = skb_clone(skb, GFP_ATOMIC); +- if (!data->evt_skb) { +- kfree_skb(skb); +- kfree(urb->setup_packet); +- return; +- } +- } +- +- err = hci_recv_frame(hdev, skb); +- if (err < 0) { +- kfree_skb(data->evt_skb); +- data->evt_skb = NULL; +- kfree(urb->setup_packet); +- return; +- } +- +- if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT, +- &data->flags)) { +- /* Barrier to sync with other CPUs */ +- smp_mb__after_atomic(); +- wake_up_bit(&data->flags, +- BTUSB_TX_WAIT_VND_EVT); +- } +- kfree(urb->setup_packet); +- return; +- } else if (urb->status == -ENOENT) { +- /* Avoid suspend failed when usb_kill_urb */ +- return; +- } +- +- usb_mark_last_busy(data->udev); +- +- /* The URB complete handler is still called with urb->actual_length = 0 +- * when the event is not available, so we should keep re-submitting +- * URB until WMT event returns, Also, It's necessary to wait some time +- * between the two consecutive control URBs to relax the target device +- * to generate the event. Otherwise, the WMT event cannot return from +- * the device successfully. +- */ +- udelay(500); +- +- usb_anchor_urb(urb, &data->ctrl_anchor); +- err = usb_submit_urb(urb, GFP_ATOMIC); +- if (err < 0) { +- kfree(urb->setup_packet); +- /* -EPERM: urb is being killed; +- * -ENODEV: device got disconnected +- */ +- if (err != -EPERM && err != -ENODEV) +- bt_dev_err(hdev, "urb %p failed to resubmit (%d)", +- urb, -err); +- usb_unanchor_urb(urb); +- } +-} +- +-static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev) +-{ +- struct btusb_data *data = hci_get_drvdata(hdev); +- struct usb_ctrlrequest *dr; +- unsigned char *buf; +- int err, size = 64; +- unsigned int pipe; +- struct urb *urb; +- +- urb = usb_alloc_urb(0, GFP_KERNEL); +- if (!urb) +- return -ENOMEM; +- +- dr = kmalloc(sizeof(*dr), GFP_KERNEL); +- if (!dr) { +- usb_free_urb(urb); +- return -ENOMEM; +- } +- +- dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; +- dr->bRequest = 1; +- dr->wIndex = cpu_to_le16(0); +- dr->wValue = cpu_to_le16(48); +- dr->wLength = cpu_to_le16(size); +- +- buf = kmalloc(size, GFP_KERNEL); +- if (!buf) { +- kfree(dr); +- usb_free_urb(urb); +- return -ENOMEM; +- } +- +- pipe = usb_rcvctrlpipe(data->udev, 0); +- +- usb_fill_control_urb(urb, data->udev, pipe, (void *)dr, +- buf, size, btusb_mtk_wmt_recv, hdev); +- +- urb->transfer_flags |= URB_FREE_BUFFER; +- +- usb_anchor_urb(urb, &data->ctrl_anchor); +- err = usb_submit_urb(urb, GFP_KERNEL); +- if (err < 0) { +- if (err != -EPERM && err != -ENODEV) +- bt_dev_err(hdev, "urb %p submission failed (%d)", +- urb, -err); +- usb_unanchor_urb(urb); +- } +- +- usb_free_urb(urb); +- +- return err; +-} +- +-static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev, +- struct btmtk_hci_wmt_params *wmt_params) +-{ +- struct btusb_data *data = hci_get_drvdata(hdev); +- struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; +- u32 hlen, status = BTMTK_WMT_INVALID; +- struct btmtk_hci_wmt_evt *wmt_evt; +- struct btmtk_hci_wmt_cmd *wc; +- struct btmtk_wmt_hdr *hdr; +- int err; +- +- /* Send the WMT command and wait until the WMT event returns */ +- hlen = sizeof(*hdr) + wmt_params->dlen; +- if (hlen > 255) +- return -EINVAL; +- +- wc = kzalloc(hlen, GFP_KERNEL); +- if (!wc) +- return -ENOMEM; +- +- hdr = &wc->hdr; +- hdr->dir = 1; +- hdr->op = wmt_params->op; +- hdr->dlen = cpu_to_le16(wmt_params->dlen + 1); +- hdr->flag = wmt_params->flag; +- memcpy(wc->data, wmt_params->data, wmt_params->dlen); +- +- set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); +- +- /* WMT cmd/event doesn't follow up the generic HCI cmd/event handling, +- * it needs constantly polling control pipe until the host received the +- * WMT event, thus, we should require to specifically acquire PM counter +- * on the USB to prevent the interface from entering auto suspended +- * while WMT cmd/event in progress. +- */ +- err = usb_autopm_get_interface(data->intf); +- if (err < 0) +- goto err_free_wc; +- +- err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc); +- +- if (err < 0) { +- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); +- usb_autopm_put_interface(data->intf); +- goto err_free_wc; +- } +- +- /* Submit control IN URB on demand to process the WMT event */ +- err = btusb_mtk_submit_wmt_recv_urb(hdev); +- +- usb_autopm_put_interface(data->intf); +- +- if (err < 0) +- goto err_free_wc; +- +- /* The vendor specific WMT commands are all answered by a vendor +- * specific event and will have the Command Status or Command +- * Complete as with usual HCI command flow control. +- * +- * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT +- * state to be cleared. The driver specific event receive routine +- * will clear that state and with that indicate completion of the +- * WMT command. +- */ +- err = wait_on_bit_timeout(&data->flags, BTUSB_TX_WAIT_VND_EVT, +- TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); +- if (err == -EINTR) { +- bt_dev_err(hdev, "Execution of wmt command interrupted"); +- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); +- goto err_free_wc; +- } +- +- if (err) { +- bt_dev_err(hdev, "Execution of wmt command timed out"); +- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); +- err = -ETIMEDOUT; +- goto err_free_wc; +- } +- +- if (data->evt_skb == NULL) +- goto err_free_wc; +- +- /* Parse and handle the return WMT event */ +- wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data; +- if (wmt_evt->whdr.op != hdr->op) { +- bt_dev_err(hdev, "Wrong op received %d expected %d", +- wmt_evt->whdr.op, hdr->op); +- err = -EIO; +- goto err_free_skb; +- } +- +- switch (wmt_evt->whdr.op) { +- case BTMTK_WMT_SEMAPHORE: +- if (wmt_evt->whdr.flag == 2) +- status = BTMTK_WMT_PATCH_UNDONE; +- else +- status = BTMTK_WMT_PATCH_DONE; +- break; +- case BTMTK_WMT_FUNC_CTRL: +- wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; +- if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) +- status = BTMTK_WMT_ON_DONE; +- else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420) +- status = BTMTK_WMT_ON_PROGRESS; +- else +- status = BTMTK_WMT_ON_UNDONE; +- break; +- case BTMTK_WMT_PATCH_DWNLD: +- if (wmt_evt->whdr.flag == 2) +- status = BTMTK_WMT_PATCH_DONE; +- else if (wmt_evt->whdr.flag == 1) +- status = BTMTK_WMT_PATCH_PROGRESS; +- else +- status = BTMTK_WMT_PATCH_UNDONE; +- break; +- } +- +- if (wmt_params->status) +- *wmt_params->status = status; +- +-err_free_skb: +- kfree_skb(data->evt_skb); +- data->evt_skb = NULL; +-err_free_wc: +- kfree(wc); +- return err; +-} +- + static int btusb_mtk_func_query(struct hci_dev *hdev) + { + struct btmtk_hci_wmt_params wmt_params; +@@ -2975,7 +2699,7 @@ static int btusb_mtk_func_query(struct hci_dev *hdev) + wmt_params.data = ¶m; + wmt_params.status = &status; + +- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); ++ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query function status (%d)", err); + return err; +@@ -3226,7 +2950,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + dev_id & 0xffff, (fw_version & 0xff) + 1); + + err = btmtk_setup_firmware_79xx(hdev, fw_bin_name, +- btusb_mtk_hci_wmt_sync); ++ btmtk_usb_hci_wmt_sync); + if (err < 0) { + bt_dev_err(hdev, "Failed to set up firmware (%d)", err); + return err; +@@ -3243,7 +2967,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + wmt_params.data = ¶m; + wmt_params.status = NULL; + +- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); ++ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; +@@ -3265,7 +2989,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + wmt_params.data = NULL; + wmt_params.status = &status; + +- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); ++ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query firmware status (%d)", err); + return err; +@@ -3278,7 +3002,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + + /* Setup a firmware which the device definitely requires */ + err = btmtk_setup_firmware(hdev, fwname, +- btusb_mtk_hci_wmt_sync); ++ btmtk_usb_hci_wmt_sync); + if (err < 0) + return err; + +@@ -3307,7 +3031,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + wmt_params.data = ¶m; + wmt_params.status = NULL; + +- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); ++ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; +@@ -3353,7 +3077,7 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev) + wmt_params.data = ¶m; + wmt_params.status = NULL; + +- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); ++ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btmtk-rename-btmediatek_data.patch b/queue-6.6/bluetooth-btmtk-rename-btmediatek_data.patch new file mode 100644 index 0000000000..9287679673 --- /dev/null +++ b/queue-6.6/bluetooth-btmtk-rename-btmediatek_data.patch @@ -0,0 +1,129 @@ +From 148c4e3a4b2c9f3c7d429d890faaf9913c13e76e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jul 2024 14:01:11 +0800 +Subject: Bluetooth: btmtk: rename btmediatek_data + +From: Chris Lu + +[ Upstream commit d3e6236053958a8f1c7c7a885d9cecdd383e4615 ] + +Rename btmediatek_data to have a consistent prefix throughout the driver. + +Signed-off-by: Sean Wang +Signed-off-by: Chris Lu +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths") +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 10 +++++----- + drivers/bluetooth/btmtk.h | 2 +- + drivers/bluetooth/btusb.c | 9 ++++----- + 3 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index 31fca4529b5ad..0290ba9ace9a9 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -64,7 +64,7 @@ static void btmtk_coredump(struct hci_dev *hdev) + + static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) + { +- struct btmediatek_data *data = hci_get_priv(hdev); ++ struct btmtk_data *data = hci_get_priv(hdev); + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n", +@@ -85,7 +85,7 @@ static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) + + static void btmtk_coredump_notify(struct hci_dev *hdev, int state) + { +- struct btmediatek_data *data = hci_get_priv(hdev); ++ struct btmtk_data *data = hci_get_priv(hdev); + + switch (state) { + case HCI_DEVCOREDUMP_IDLE: +@@ -355,7 +355,7 @@ EXPORT_SYMBOL_GPL(btmtk_set_bdaddr); + + void btmtk_reset_sync(struct hci_dev *hdev) + { +- struct btmediatek_data *reset_work = hci_get_priv(hdev); ++ struct btmtk_data *reset_work = hci_get_priv(hdev); + int err; + + hci_dev_lock(hdev); +@@ -371,7 +371,7 @@ EXPORT_SYMBOL_GPL(btmtk_reset_sync); + int btmtk_register_coredump(struct hci_dev *hdev, const char *name, + u32 fw_version) + { +- struct btmediatek_data *data = hci_get_priv(hdev); ++ struct btmtk_data *data = hci_get_priv(hdev); + + if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) + return -EOPNOTSUPP; +@@ -387,7 +387,7 @@ EXPORT_SYMBOL_GPL(btmtk_register_coredump); + + int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb) + { +- struct btmediatek_data *data = hci_get_priv(hdev); ++ struct btmtk_data *data = hci_get_priv(hdev); + int err; + bool complete = false; + +diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h +index e76b8a358be88..dde6fbdeb2b3b 100644 +--- a/drivers/bluetooth/btmtk.h ++++ b/drivers/bluetooth/btmtk.h +@@ -135,7 +135,7 @@ struct btmtk_coredump_info { + int state; + }; + +-struct btmediatek_data { ++struct btmtk_data { + u32 dev_id; + btmtk_reset_sync_func_t reset_sync; + struct btmtk_coredump_info cd_info; +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index 04eaf7abf75c4..4413ff997aa08 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -3137,7 +3137,7 @@ static int btusb_mtk_subsys_reset(struct hci_dev *hdev, u32 dev_id) + static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) + { + struct btusb_data *data = hci_get_drvdata(hdev); +- struct btmediatek_data *mtk_data; ++ struct btmtk_data *btmtk_data = hci_get_priv(hdev); + int err; + + /* It's MediaTek specific bluetooth reset mechanism via USB */ +@@ -3152,9 +3152,8 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) + + btusb_stop_traffic(data); + usb_kill_anchored_urbs(&data->tx_anchor); +- mtk_data = hci_get_priv(hdev); + +- err = btusb_mtk_subsys_reset(hdev, mtk_data->dev_id); ++ err = btusb_mtk_subsys_reset(hdev, btmtk_data->dev_id); + + usb_queue_reset_device(data->intf); + clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags); +@@ -3176,7 +3175,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev) + char fw_bin_name[64]; + u32 fw_version = 0; + u8 param; +- struct btmediatek_data *mediatek; ++ struct btmtk_data *mediatek; + + calltime = ktime_get(); + +@@ -4434,7 +4433,7 @@ static int btusb_probe(struct usb_interface *intf, + data->recv_event = btusb_recv_event_realtek; + } else if (id->driver_info & BTUSB_MEDIATEK) { + /* Allocate extra space for Mediatek device */ +- priv_size += sizeof(struct btmediatek_data); ++ priv_size += sizeof(struct btmtk_data); + } + + data->recv_acl = hci_recv_frame; +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btusb-mediatek-refactor-the-function-btusb.patch b/queue-6.6/bluetooth-btusb-mediatek-refactor-the-function-btusb.patch new file mode 100644 index 0000000000..77247ffca4 --- /dev/null +++ b/queue-6.6/bluetooth-btusb-mediatek-refactor-the-function-btusb.patch @@ -0,0 +1,95 @@ +From af1b7942b2f6ad16759e19bf99095a25206291ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 May 2024 16:15:19 -0700 +Subject: Bluetooth: btusb: mediatek: refactor the function btusb_mtk_reset + +From: Hao Qin + +[ Upstream commit 4c0c28f2bbec0c51395fd1f13c697da67483964b ] + +Extract the function btusb_mtk_subsys_reset from the btusb_mtk_reset +for the future handling of resetting bluetooth controller without +the USB reset. + +Co-developed-by: Sean Wang +Signed-off-by: Sean Wang +Signed-off-by: Hao Qin +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths") +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btusb.c | 45 +++++++++++++++++++++++---------------- + 1 file changed, 27 insertions(+), 18 deletions(-) + +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index c51523d780e65..04eaf7abf75c4 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -3081,28 +3081,13 @@ static u32 btusb_mtk_reset_done(struct hci_dev *hdev) + return val & MTK_BT_RST_DONE; + } + +-static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) ++static int btusb_mtk_subsys_reset(struct hci_dev *hdev, u32 dev_id) + { + struct btusb_data *data = hci_get_drvdata(hdev); +- struct btmediatek_data *mediatek; + u32 val; + int err; + +- /* It's MediaTek specific bluetooth reset mechanism via USB */ +- if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) { +- bt_dev_err(hdev, "last reset failed? Not resetting again"); +- return -EBUSY; +- } +- +- err = usb_autopm_get_interface(data->intf); +- if (err < 0) +- return err; +- +- btusb_stop_traffic(data); +- usb_kill_anchored_urbs(&data->tx_anchor); +- mediatek = hci_get_priv(hdev); +- +- if (mediatek->dev_id == 0x7925) { ++ if (dev_id == 0x7925) { + btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val); + val |= (1 << 5); + btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val); +@@ -3146,8 +3131,32 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) + if (!val) + bt_dev_err(hdev, "Can't get device id, subsys reset fail."); + +- usb_queue_reset_device(data->intf); ++ return err; ++} + ++static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) ++{ ++ struct btusb_data *data = hci_get_drvdata(hdev); ++ struct btmediatek_data *mtk_data; ++ int err; ++ ++ /* It's MediaTek specific bluetooth reset mechanism via USB */ ++ if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) { ++ bt_dev_err(hdev, "last reset failed? Not resetting again"); ++ return -EBUSY; ++ } ++ ++ err = usb_autopm_get_interface(data->intf); ++ if (err < 0) ++ return err; ++ ++ btusb_stop_traffic(data); ++ usb_kill_anchored_urbs(&data->tx_anchor); ++ mtk_data = hci_get_priv(hdev); ++ ++ err = btusb_mtk_subsys_reset(hdev, mtk_data->dev_id); ++ ++ usb_queue_reset_device(data->intf); + clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags); + + return err; +-- +2.53.0 + diff --git a/queue-6.6/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch b/queue-6.6/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch new file mode 100644 index 0000000000..85d713599c --- /dev/null +++ b/queue-6.6/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch @@ -0,0 +1,82 @@ +From 8064980dcc9142351b5c9aa23734cd349c0a352f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 23:56:26 +0900 +Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx + +From: Xingwang Xiang + +[ Upstream commit ddf8029623a1af20e984c040e89ff918158397ab ] + +sk_psock_strp_data_ready() already checks tls_sw_has_ctx_rx() and +defers to psock->saved_data_ready when a TLS RX context is present, +avoiding a conflict with the TLS strparser's ownership of the receive +queue (commit e91de6afa81c, "bpf: Fix running sk_skb program types +with ktls"). + +sk_psock_verdict_data_ready() has no equivalent guard. When a socket +is inserted into a sockmap (BPF_SK_SKB_VERDICT) before TLS RX is +configured, tls_sw_strparser_arm() saves sk_psock_verdict_data_ready +as rx_ctx->saved_data_ready. On data arrival: + + tls_data_ready -> tls_strp_data_ready -> tls_rx_msg_ready + -> saved_data_ready() = sk_psock_verdict_data_ready() + -> tcp_read_skb() drains sk_receive_queue via __skb_unlink() + without calling tcp_eat_skb(), so copied_seq is not advanced. + +tls_strp_msg_load() then finds tcp_inq() >= full_len (stale), calls +tcp_recv_skb() on the now-empty queue, hits WARN_ON_ONCE(!first), and +returns with rx_ctx->strp.anchor.frag_list pointing at a psock-owned +(potentially freed) skb. tls_decrypt_sg() subsequently walks that +frag_list: use-after-free. + +Apply the same fix as sk_psock_strp_data_ready(): if a TLS RX context +is present, call psock->saved_data_ready (sock_def_readable) to wake +recv() waiters and return immediately, leaving the receive queue +untouched. TLS retains sole ownership of the queue and decrypts the +record normally through tls_sw_recvmsg(). + +Fixes: ef5659280eb1 ("bpf, sockmap: Allow skipping sk_skb parser program") +Signed-off-by: Xingwang Xiang +Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/skmsg.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/core/skmsg.c b/net/core/skmsg.c +index 14208d32eeaf3..403ad91c5c74c 100644 +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -1267,12 +1267,19 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) + static void sk_psock_verdict_data_ready(struct sock *sk) + { + const struct proto_ops *ops = NULL; ++ struct sk_psock *psock; + struct socket *sock; + int copied; + + trace_sk_data_ready(sk); + + rcu_read_lock(); ++ psock = sk_psock(sk); ++ if (psock && tls_sw_has_ctx_rx(sk)) { ++ psock->saved_data_ready(sk); ++ rcu_read_unlock(); ++ return; ++ } + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); +@@ -1282,8 +1289,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) + + copied = ops->read_skb(sk, sk_psock_verdict_recv); + if (copied >= 0) { +- struct sk_psock *psock; +- + rcu_read_lock(); + psock = sk_psock(sk); + if (psock) +-- +2.53.0 + diff --git a/queue-6.6/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-6.6/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..0d98ec74a8 --- /dev/null +++ b/queue-6.6/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From 70f117e9f9be084efd4d33f78bb594688a924da2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index f0a0f24f2172a..9c91b663bc3bd 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4641,10 +4641,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4652,7 +4666,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4669,7 +4682,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4677,8 +4690,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-6.6/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch b/queue-6.6/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch new file mode 100644 index 0000000000..6b4e433f88 --- /dev/null +++ b/queue-6.6/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch @@ -0,0 +1,75 @@ +From e4c5ce2572be577b32e770a39590962d5d28b2a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:58:56 +0100 +Subject: btrfs: tracepoints: fix sleep while in atomic context in + btrfs_sync_file() + +From: Filipe Manana + +[ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ] + +The trace event btrfs_sync_file() is called in an atomic context (all trace +events are) and its call to dput(), which is needed due to the call to +dget_parent(), can sleep, triggering a kernel splat. + +This can be reproduced by enabling the trace event and running btrfs/056 +from fstests for example. The splat shown in dmesg is the following: + + [53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 + [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io + [53.988] preempt_count: 2, expected: 0 + [53.967] RCU nest depth: 0, expected: 0 + [53.943] Preemption disabled at: + [53.944] [<0000000000000000>] 0x0 + [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) + [54.070] Tainted: [W]=WARN + [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 + [54.072] Call Trace: + [54.074] + [54.076] dump_stack_lvl+0x56/0x80 + [54.079] __might_resched.cold+0xd6/0x10f + [54.072] dput.part.0+0x24/0x110 + [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] + [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] + [54.087] ? __handle_mm_fault+0x8ae/0xed0 + [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] + [54.091] vfs_write+0x21f/0x450 + [54.094] __x64_sys_pwrite64+0x8d/0xc0 + [54.096] ? do_user_addr_fault+0x20c/0x670 + [54.099] do_syscall_64+0x60/0xf20 + [54.092] ? clear_bhb_loop+0x60/0xb0 + [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e + +So stop using dget_parent() and dput() and access the parent dentry +directly as dentry->d_parent. This is also what ext4 is doing in +its equivalent trace event ext4_sync_file_enter(). + +Fixes: a85b46db143f ("btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()") +Reviewed-by: Boris Burkov +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + include/trace/events/btrfs.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h +index 2364c68df76c4..e06dbddc2872f 100644 +--- a/include/trace/events/btrfs.h ++++ b/include/trace/events/btrfs.h +@@ -791,10 +791,8 @@ TRACE_EVENT(btrfs_sync_file, + TP_fast_assign( + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); +- struct dentry *parent = dget_parent(dentry); +- struct inode *parent_inode = d_inode(parent); ++ struct inode *parent_inode = d_inode(dentry->d_parent); + +- dput(parent); + TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); + __entry->ino = btrfs_ino(BTRFS_I(inode)); + __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); +-- +2.53.0 + diff --git a/queue-6.6/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch b/queue-6.6/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch new file mode 100644 index 0000000000..b00a0c3a0f --- /dev/null +++ b/queue-6.6/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch @@ -0,0 +1,50 @@ +From 5ce381fbc7cf4d09fc5faba511324a544290c44f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 20:21:38 +0300 +Subject: drm/msm/dsi: don't dump registers past the mapped region + +From: Dmitry Baryshkov + +[ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] + +On DSI 6G platforms the IO address space is internally adjusted by +io_offset. Later this adjusted address might be used for memory dumping. +However the size that is used for memory dumping isn't adjusted to +account for the io_offset, leading to the potential access to the +unmapped region. Lower ctrl_size by the io_offset value to prevent +access past the mapped area. + + msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) + msm_dsi_host_snapshot+0x4c/0x78 [msm] + msm_dsi_snapshot+0x28/0x50 [msm] + msm_disp_snapshot_capture_state+0x74/0x140 [msm] + msm_disp_snapshot_state_sync+0x60/0x90 [msm] + _msm_disp_snapshot_work+0x30/0x90 [msm] + kthread_worker_fn+0xdc/0x460 + kthread+0x120/0x140 + +Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Patchwork: https://patchwork.freedesktop.org/patch/721747/ +Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c +index 771ac34561037..f5ac0e20665f7 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_host.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_host.c +@@ -1929,6 +1929,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) + + /* fixup base address by io offset */ + msm_host->ctrl_base += cfg->io_offset; ++ msm_host->ctrl_size -= cfg->io_offset; + + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, +-- +2.53.0 + diff --git a/queue-6.6/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-6.6/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..8aa4ec6b1b --- /dev/null +++ b/queue-6.6/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,51 @@ +From 1b41d87079fad5bc247d4a0f0119585e03c23866 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index d5512037c38bc..10d068fdfebd0 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -362,14 +362,15 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, size_t len, int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + /* The arm-smmu driver expects the addresses to be sign extended */ + if (iova & BIT_ULL(48)) + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.6/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-6.6/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..2380ea71c9 --- /dev/null +++ b/queue-6.6/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From 5656da4a04984de5795568cce1f76a890394b708 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index 4d55e3cf570f0..a966a03167cc0 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -9,7 +9,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + if (!(*reg)) + *reg = kvzalloc(len_padded, GFP_KERNEL); +@@ -51,8 +51,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -61,6 +61,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -70,6 +71,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -189,7 +201,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-6.6/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-6.6/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..dd7cc66a75 --- /dev/null +++ b/queue-6.6/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From 37641df58abd3266b5da7df0e9fd47269ec8b299 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-6.6/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-6.6/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..c58baee2f8 --- /dev/null +++ b/queue-6.6/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From 9e2bb4c8e09d31dbdf970bdb76f579794dc4e3ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index d885e1381072a..31e61602e7702 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -24,6 +24,8 @@ static int ffa_device_match(struct device *dev, struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -107,7 +109,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-6.6/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-6.6/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..ae6904c2ee --- /dev/null +++ b/queue-6.6/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From b2476dd0ee8670e2cf1c59b16fec55689cb93624 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 488f8345dd1b6..ece91d8d820b5 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -743,7 +743,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(RXTX_BUFFER_SIZE, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.6/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-6.6/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..7efeea0b51 --- /dev/null +++ b/queue-6.6/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From cf33a17085ec9a5af440069f896b4264091f248c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index f9a88b239301e..c6689f66a2d29 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1374,6 +1374,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1381,9 +1382,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-6.6/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch b/queue-6.6/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch new file mode 100644 index 0000000000..0f83d98562 --- /dev/null +++ b/queue-6.6/gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch @@ -0,0 +1,70 @@ +From 9c8adb54eb942900115bdd40fc4576e114195136 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Nov 2024 22:16:15 +0200 +Subject: gpiolib: cdev: use !mem_is_zero() instead of memchr_inv(s, 0, n) + +From: Andy Shevchenko + +[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ] + +Use the mem_is_zero() helper where possible. + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 545998e9f6ad2..f9a88b239301e 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -25,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1381,7 +1381,7 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + +- if (memchr_inv(lc->padding, 0, sizeof(lc->padding))) ++ if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + + for (i = 0; i < num_lines; i++) { +@@ -1825,7 +1825,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) + if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX)) + return -EINVAL; + +- if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding))) ++ if (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) + return -EINVAL; + + lc = &ulr.config; +@@ -2619,7 +2619,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + +- if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) ++ if (!mem_is_zero(lineinfo.padding, sizeof(lineinfo.padding))) + return -EINVAL; + + desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); +-- +2.53.0 + diff --git a/queue-6.6/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-6.6/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..4aa70b6c01 --- /dev/null +++ b/queue-6.6/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From b9e545df738d3cd064e374fa51230340d341111a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index d9e33dde89899..9d396d2e534d0 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -234,7 +234,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-6.6/hid-uclogic-fix-regression-of-input-name-assignment.patch b/queue-6.6/hid-uclogic-fix-regression-of-input-name-assignment.patch new file mode 100644 index 0000000000..74b986b302 --- /dev/null +++ b/queue-6.6/hid-uclogic-fix-regression-of-input-name-assignment.patch @@ -0,0 +1,44 @@ +From c5a8f43403e7d2d1cf74e72563c5948237d5e79e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 10:33:16 +0200 +Subject: HID: uclogic: Fix regression of input name assignment + +From: Takashi Iwai + +[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] + +The previous fix for adding the devm_kasprintf() return check in the +commit bd07f751208b ("HID: uclogic: Add NULL check in +uclogic_input_configured()") changed the condition of hi->input->name +assignment, and it resulted in missing the proper input device name +when no custom suffix is defined. + +Restore the conditional to the original content to address the +regression. + +Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") +Signed-off-by: Takashi Iwai +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-uclogic-core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c +index 45de01dea4b1c..d4cf8846f326a 100644 +--- a/drivers/hid/hid-uclogic-core.c ++++ b/drivers/hid/hid-uclogic-core.c +@@ -142,7 +142,9 @@ static int uclogic_input_configured(struct hid_device *hdev, + suffix = "System Control"; + break; + } +- } else { ++ } ++ ++ if (suffix) { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) +-- +2.53.0 + diff --git a/queue-6.6/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-6.6/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..8ece386e60 --- /dev/null +++ b/queue-6.6/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From 5393eb8a08fb8da31ae51878187989bdb85c0f62 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index 850db8e0e6b00..14d765247d73c 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + int ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-6.6/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch b/queue-6.6/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch new file mode 100644 index 0000000000..ec1b8a5d27 --- /dev/null +++ b/queue-6.6/ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch @@ -0,0 +1,57 @@ +From a1e38ab8eff054c3b59a90aec95bed1365cef369 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 23:03:28 -0400 +Subject: ipv6: route: Unregister netdevice notifier on BPF init failure + +From: Yuho Choi + +[ Upstream commit 1341db322417266fb5845df81d28305b83a37324 ] + +ip6_route_init() registers ip6_route_dev_notifier before registering the +IPv6 route BPF iterator target. If bpf_iter_register() fails after the +notifier has been registered, the error path currently jumps to +out_register_late_subsys and unwinds the RTNL handlers and pernet route +state without removing the notifier from the netdevice notifier chain. + +This leaves ip6_route_dev_notify() callable after the IPv6 route state it +uses has been torn down. Add a separate unwind label for the BPF iterator +failure path and unregister the netdevice notifier before continuing with +the existing cleanup. + +Fixes: 138d0be35b14 ("net: bpf: Add netlink and ipv6_route bpf_iter targets") +Signed-off-by: Yuho Choi +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/route.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index c5b71baf95e7b..e10810b36484a 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -6818,7 +6818,7 @@ int __init ip6_route_init(void) + #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) + ret = bpf_iter_register(); + if (ret) +- goto out_register_late_subsys; ++ goto out_register_notifier; + #endif + #endif + +@@ -6833,6 +6833,10 @@ int __init ip6_route_init(void) + out: + return ret; + ++#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) ++out_register_notifier: ++ unregister_netdevice_notifier(&ip6_route_dev_notifier); ++#endif + out_register_late_subsys: + rtnl_unregister_all(PF_INET6); + unregister_pernet_subsys(&ip6_route_net_late_ops); +-- +2.53.0 + diff --git a/queue-6.6/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch b/queue-6.6/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch new file mode 100644 index 0000000000..1ed42a98a5 --- /dev/null +++ b/queue-6.6/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch @@ -0,0 +1,68 @@ +From ef81ddb202205d6af82f6de9f7ed6ea9214d8ba5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Mar 2026 15:32:29 +0800 +Subject: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jiayuan Chen + +[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] + +On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via +run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. + +After irq_work_single() clears BUSY via atomic_cmpxchg(), it still +dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). + +An irq_work_sync() caller on another CPU that enters after BUSY is cleared +can observe BUSY==0 immediately, return, and free the work before those +accesses complete — causing a use-after-free. + +Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire +irq_work_single() execution is within an RCU read-side critical +section. Then add synchronize_rcu() in irq_work_sync() after +rcuwait_wait_event() to ensure the caller waits for the RCU grace period +before returning, preventing premature frees. + +Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") +Suggested-by: Sebastian Andrzej Siewior +Suggested-by: Steven Rostedt +Signed-off-by: Jiayuan Chen +Signed-off-by: Thomas Gleixner +Reviewed-by: Sebastian Andrzej Siewior +Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev +Signed-off-by: Sasha Levin +--- + kernel/irq_work.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/kernel/irq_work.c b/kernel/irq_work.c +index 2f4fb336dda17..188721af8eb31 100644 +--- a/kernel/irq_work.c ++++ b/kernel/irq_work.c +@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) + !arch_irq_work_has_interrupt()) { + rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), + TASK_UNINTERRUPTIBLE); ++ /* ++ * Ensure irq_work_single() does not access @work ++ * after removing IRQ_WORK_BUSY. It is always ++ * accessed within a RCU-read section. ++ */ ++ synchronize_rcu(); + return; + } + +@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); + + static void run_irq_workd(unsigned int cpu) + { ++ guard(rcu)(); + irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + +-- +2.53.0 + diff --git a/queue-6.6/irqchip-ath79-cpu-remove-unused-function.patch b/queue-6.6/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..bee21bdf1c --- /dev/null +++ b/queue-6.6/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From f7b25beca0f0523d75fb5d9f03cf577da87822a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-6.6/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch b/queue-6.6/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch new file mode 100644 index 0000000000..4a84cd359c --- /dev/null +++ b/queue-6.6/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch @@ -0,0 +1,103 @@ +From 3db2e5b555abafc1ae750c6e0389042ca453f4a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() + +From: Jianpeng Chang + +[ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ] + +When kprobe_add_area_blacklist() iterates through a section like +.kprobes.text, the start address may not correspond to a named symbol. +On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by +commit baaf553d3bc3 ("arm64: Implement +HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag +-fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry +point for ftrace call_ops. These pre-function NOPs sit at the section base +address, before the first named function symbol. The compiler emits a $x +mapping symbol at offset 0x00 to mark the start of code, but +find_kallsyms_symbol() ignores mapping symbols. + +Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no +pre-function NOPs are inserted, the first function starts at offset +0x00, and the bug does not trigger. + +This only affects modules that have a .kprobes.text section (i.e. those +using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead +(like kretprobe_example.ko) blacklist exact function addresses via the +_kprobe_blacklist section and are not affected. + +For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, +the .kprobes.text section layout is: + + offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) + offset 0x08: handler_post (64 bytes) + offset 0x50: handler_pre (68 bytes) + +kprobe_add_area_blacklist() starts iterating from the section base +address (offset 0x00), which only has the $x mapping symbol. +kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() +for this address, which goes through: + + kallsyms_lookup_size_offset() + -> module_address_lookup() + -> find_kallsyms_symbol() + +find_kallsyms_symbol() scans all module symbols to find the closest +preceding symbol. + +Since no named text symbol exists at offset 0x00, +find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol +whose address is in the temporary image) as the "best" match. The +computed "size" = next_text_symbol - modinfo_symbol spans across +these two unrelated memory regions, creating a blacklist entry with +a bogus range of tens of terabytes. + +Whether this causes a visible failure depends on address randomization, +here is what happens on Raspberry Pi 4/5: + + - On RPi5, the bogus size was ~35 TB. start + size stayed within + 64-bit range, so the blacklist entry covered the entire kernel + text. register_kprobe() in the module's own init function failed + with -EINVAL. + + - On RPi4, the bogus size was ~75 TB. start + size overflowed + 64 bits and wrapped to a small address near zero. The range + check (addr >= start && addr < end) then failed because end + wrapped around, so the bogus entry was accidentally harmless + and kprobes worked by luck. + +The same bug exists on both machines, but randomization determines whether +the integer overflow masks it or not. + +Fix this by adding notrace to the __kprobes macro. Functions in +.kprobes.text are kprobe infrastructure handlers that should never be +traced by ftrace. With notrace, the compiler stops inserting them and the +non-symbol gap at the section start disappears entirely. + +Link: https://lore.kernel.org/all/20260506012706.2785785-1-jianpeng.chang.cn@windriver.com/ + +Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") +Signed-off-by: Jianpeng Chang +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/asm-generic/kprobes.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h +index 060eab094e5a2..5290a2b2e15a0 100644 +--- a/include/asm-generic/kprobes.h ++++ b/include/asm-generic/kprobes.h +@@ -14,7 +14,7 @@ static unsigned long __used \ + _kbl_addr_##fname = (unsigned long)fname; + # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) + /* Use this to forbid a kprobes attach on very low level functions */ +-# define __kprobes __section(".kprobes.text") ++# define __kprobes notrace __section(".kprobes.text") + # define nokprobe_inline __always_inline + #else + # define NOKPROBE_SYMBOL(fname) +-- +2.53.0 + diff --git a/queue-6.6/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-6.6/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..f1271d550b --- /dev/null +++ b/queue-6.6/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From dc4ebeaf10e9fa75c150c268b22b767955bd5f7a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 68a6daec0aef1..fab3458c54e4c 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-6.6/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-6.6/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..0abd4ec19f --- /dev/null +++ b/queue-6.6/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From e2f2a935ab368b6236a999d1d436eaef67f1dc62 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index fab3458c54e4c..f5ba590373907 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-6.6/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch b/queue-6.6/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch new file mode 100644 index 0000000000..8bcca8c8d7 --- /dev/null +++ b/queue-6.6/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch @@ -0,0 +1,76 @@ +From f20d8291ce98cfa8a7a3b0e449d84fdf5ef80680 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 15:05:07 +0800 +Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions + +From: Tiezhu Yang + +[ Upstream commit 1c856e158fd34ef2c4475a81c1dc386329989938 ] + +KPROBE_HIT_SS and KPROBE_REENTER are two types of fatal recursions that +can not be safely recovered in kprobes. + +KPROBE_HIT_SS means that a kprobe is hit during single-stepping. At +this point, the architecture-specific single-step context is already +active. Nested single-stepping would corrupt the state, as the kprobe +control block (kcb) and hardware registers cannot safely store multiple +levels of stepping state. + +KPROBE_REENTER means that a third-level recursion occurs when a probe +is hit while the system is already handling a nested probe (second- +level). The kcb only provides a single slot (prev_kprobe) to backup the +state. When a third probe is hit, there is no more space to save the +state without corrupting the first-level backup. + +Kprobes work by replacing instructions with breakpoints. In order to +execute the original instruction and continue, it must be moved to a +temporary "single-step" slot. Since there is no backup space left to +set up this slot safely, the CPU would be forced to return to the same +original breakpoint address, triggering an endless loop. + +Currently, the code only prints a warning and returns. This leads to +an infinite re-entry loop as the CPU repeatedly hits the same trap and +a "stuck" CPU core because preemption was disabled at the start of the +handler and never re-enabled in this early return path. + +Fix the logic by: +1. Merging KPROBE_HIT_SS and KPROBE_REENTER cases, as both represent + fatal recursions that cannot be safely recovered. +2. Replacing WARN_ON_ONCE() with BUG() to terminate the system. This + aligns LoongArch with other architectures (x86, arm64, riscv) and + prevents stack overflow while providing diagnostic information. + +Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") +Signed-off-by: Tiezhu Yang +Signed-off-by: Huacai Chen +Signed-off-by: Sasha Levin +--- + arch/loongarch/kernel/kprobes.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c +index 17b040bd6067c..ea98b6a3e201e 100644 +--- a/arch/loongarch/kernel/kprobes.c ++++ b/arch/loongarch/kernel/kprobes.c +@@ -184,16 +184,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) + { + switch (kcb->kprobe_status) { +- case KPROBE_HIT_SS: + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs, kcb, 1); + break; ++ case KPROBE_HIT_SS: + case KPROBE_REENTER: + pr_warn("Failed to recover from reentered kprobes.\n"); + dump_kprobe(p); +- WARN_ON_ONCE(1); ++ BUG(); + break; + default: + WARN_ON(1); +-- +2.53.0 + diff --git a/queue-6.6/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-6.6/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..97ed540f8a --- /dev/null +++ b/queue-6.6/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From a02ad5089d1017829c94f930899595d91a7d2f85 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index baf12ae0b8c4c..576e75b5168e8 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1880,6 +1880,9 @@ static int ag71xx_probe(struct platform_device *pdev) + return -ENOMEM; + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-6.6/net-bridge-flush-multicast-groups-when-snooping-is-d.patch b/queue-6.6/net-bridge-flush-multicast-groups-when-snooping-is-d.patch new file mode 100644 index 0000000000..7503617302 --- /dev/null +++ b/queue-6.6/net-bridge-flush-multicast-groups-when-snooping-is-d.patch @@ -0,0 +1,67 @@ +From 6e9d5a749306867e684d3df57d5737408c5807a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Oct 2025 16:45:37 +0200 +Subject: net: bridge: Flush multicast groups when snooping is disabled + +From: Petr Machata + +[ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] + +When forwarding multicast packets, the bridge takes MDB into account when +IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the +MDB is retained, even though it is not used anymore. + +At the same time, during the time that snooping is disabled, the IGMP / MLD +control packets are obviously ignored, and after the snooping is reenabled, +the administrator has to assume it is out of sync. In particular, missed +join and leave messages would lead to traffic being forwarded to wrong +interfaces. + +Keeping the MDB entries around thus serves no purpose, and just takes +memory. Note also that disabling per-VLAN snooping does actually flush the +relevant MDB entries. + +This patch flushes non-permanent MDB entries as global snooping is +disabled. + +Signed-off-by: Petr Machata +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 4e75ec75c7021..f0a0f24f2172a 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4641,6 +4641,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + ++static void br_multicast_del_grps(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_disable_port_ctx(&port->multicast_ctx); ++} ++ + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +@@ -4661,6 +4669,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; ++ br_multicast_del_grps(br); + goto unlock; + } + +-- +2.53.0 + diff --git a/queue-6.6/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-6.6/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..7f9fee42a1 --- /dev/null +++ b/queue-6.6/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From 04b7baddcd8d22139ed597c08f1a63f8d426185b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 2d18a03d92742..ada56e432dd7a 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -880,12 +880,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2419,6 +2423,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2598,6 +2604,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-6.6/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-6.6/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..13eb5f6085 --- /dev/null +++ b/queue-6.6/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From 884a3599d988f18af0fc5dfd1537154183b4cbc5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index ed783943c73c7..8d889bc5f3d1b 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1174,37 +1174,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-6.6/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch b/queue-6.6/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch new file mode 100644 index 0000000000..05d76d89a7 --- /dev/null +++ b/queue-6.6/net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch @@ -0,0 +1,185 @@ +From ded777f1581a284e996d1f102597d987668620c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Apr 2024 10:15:11 +0300 +Subject: net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to mt753x_to_cpu_fw +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Arınç ÜNAL + +[ Upstream commit 7603a0c7d2210a253265394b50567c64fbb977e4 ] + +The mt753x_bpdu_port_fw enum is globally used for manipulating the process +of deciding the forwardable ports, specifically concerning the CPU port(s). +Therefore, rename it and the values in it to mt753x_to_cpu_fw. + +Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents. + +Signed-off-by: Arınç ÜNAL +Signed-off-by: David S. Miller +Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames") +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 44 ++++++++++------------- + drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++-------------------- + 2 files changed, 56 insertions(+), 64 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index ada56e432dd7a..ed783943c73c7 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1178,42 +1178,34 @@ mt753x_trap_frames(struct mt7530_priv *priv) + * VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_BPC, +- MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK | +- MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK | +- MT753X_BPDU_PORT_FW_MASK, +- MT753X_PAE_BPDU_FR | +- MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | ++ BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC1, +- MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK | +- MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK, +- MT753X_R02_BPDU_FR | +- MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | ++ R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | ++ R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC2, +- MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK | +- MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK, +- MT753X_R0E_BPDU_FR | +- MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | ++ R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | ++ R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + } + + static int +diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h +index 0ad52d3cbfebb..393a7cb03a432 100644 +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -67,47 +67,47 @@ enum mt753x_id { + #define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + +-/* Registers for BPDU and PAE frame control*/ ++/* Register for BPDU and PAE frame control */ + #define MT753X_BPC 0x24 +-#define MT753X_PAE_BPDU_FR BIT(25) +-#define MT753X_PAE_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_PAE_EG_TAG(x) FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x) +-#define MT753X_PAE_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_PAE_PORT_FW(x) FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) +-#define MT753X_BPDU_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_BPDU_EG_TAG(x) FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x) +-#define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :01 and :02 MAC DA frame control */ ++#define PAE_BPDU_FR BIT(25) ++#define PAE_EG_TAG_MASK GENMASK(24, 22) ++#define PAE_EG_TAG(x) FIELD_PREP(PAE_EG_TAG_MASK, x) ++#define PAE_PORT_FW_MASK GENMASK(18, 16) ++#define PAE_PORT_FW(x) FIELD_PREP(PAE_PORT_FW_MASK, x) ++#define BPDU_EG_TAG_MASK GENMASK(8, 6) ++#define BPDU_EG_TAG(x) FIELD_PREP(BPDU_EG_TAG_MASK, x) ++#define BPDU_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */ + #define MT753X_RGAC1 0x28 +-#define MT753X_R02_BPDU_FR BIT(25) +-#define MT753X_R02_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R02_EG_TAG(x) FIELD_PREP(MT753X_R02_EG_TAG_MASK, x) +-#define MT753X_R02_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R02_PORT_FW(x) FIELD_PREP(MT753X_R02_PORT_FW_MASK, x) +-#define MT753X_R01_BPDU_FR BIT(9) +-#define MT753X_R01_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R01_EG_TAG(x) FIELD_PREP(MT753X_R01_EG_TAG_MASK, x) +-#define MT753X_R01_PORT_FW_MASK GENMASK(2, 0) +- +-/* Register for :03 and :0E MAC DA frame control */ ++#define R02_BPDU_FR BIT(25) ++#define R02_EG_TAG_MASK GENMASK(24, 22) ++#define R02_EG_TAG(x) FIELD_PREP(R02_EG_TAG_MASK, x) ++#define R02_PORT_FW_MASK GENMASK(18, 16) ++#define R02_PORT_FW(x) FIELD_PREP(R02_PORT_FW_MASK, x) ++#define R01_BPDU_FR BIT(9) ++#define R01_EG_TAG_MASK GENMASK(8, 6) ++#define R01_EG_TAG(x) FIELD_PREP(R01_EG_TAG_MASK, x) ++#define R01_PORT_FW_MASK GENMASK(2, 0) ++ ++/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */ + #define MT753X_RGAC2 0x2c +-#define MT753X_R0E_BPDU_FR BIT(25) +-#define MT753X_R0E_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R0E_EG_TAG(x) FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x) +-#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) +-#define MT753X_R03_BPDU_FR BIT(9) +-#define MT753X_R03_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R03_EG_TAG(x) FIELD_PREP(MT753X_R03_EG_TAG_MASK, x) +-#define MT753X_R03_PORT_FW_MASK GENMASK(2, 0) +- +-enum mt753x_bpdu_port_fw { +- MT753X_BPDU_FOLLOW_MFC, +- MT753X_BPDU_CPU_EXCLUDE = 4, +- MT753X_BPDU_CPU_INCLUDE = 5, +- MT753X_BPDU_CPU_ONLY = 6, +- MT753X_BPDU_DROP = 7, ++#define R0E_BPDU_FR BIT(25) ++#define R0E_EG_TAG_MASK GENMASK(24, 22) ++#define R0E_EG_TAG(x) FIELD_PREP(R0E_EG_TAG_MASK, x) ++#define R0E_PORT_FW_MASK GENMASK(18, 16) ++#define R0E_PORT_FW(x) FIELD_PREP(R0E_PORT_FW_MASK, x) ++#define R03_BPDU_FR BIT(9) ++#define R03_EG_TAG_MASK GENMASK(8, 6) ++#define R03_EG_TAG(x) FIELD_PREP(R03_EG_TAG_MASK, x) ++#define R03_PORT_FW_MASK GENMASK(2, 0) ++ ++enum mt753x_to_cpu_fw { ++ TO_CPU_FW_SYSTEM_DEFAULT, ++ TO_CPU_FW_CPU_EXCLUDE = 4, ++ TO_CPU_FW_CPU_INCLUDE = 5, ++ TO_CPU_FW_CPU_ONLY = 6, ++ TO_CPU_FW_DROP = 7, + }; + + /* Registers for address table access */ +-- +2.53.0 + diff --git a/queue-6.6/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-6.6/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..654ec73748 --- /dev/null +++ b/queue-6.6/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 0b4454486fdb78828ec1ea617095e8e045217880 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 0071f27d2d6c1..45a548d93e152 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1449,6 +1450,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1462,7 +1464,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1502,6 +1503,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1512,6 +1514,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1546,6 +1549,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1554,6 +1558,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1563,6 +1568,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1892,6 +1898,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.6/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-6.6/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..1d35487bcb --- /dev/null +++ b/queue-6.6/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,53 @@ +From 8f5ac27dc7284539846639402544e320cb545380 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 02beed666fa63..0071f27d2d6c1 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1498,6 +1498,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-6.6/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-6.6/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..22b7673189 --- /dev/null +++ b/queue-6.6/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From 221b033be0f077993d7f62e71f9dfc424682b51a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index fce2ff1e1d834..02beed666fa63 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -121,6 +121,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1447,10 +1449,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1504,6 +1506,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1554,6 +1557,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1882,6 +1886,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-6.6/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-6.6/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..5c9c3c68b2 --- /dev/null +++ b/queue-6.6/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From 8cd5cac8e4283df7559bcf017a050b5aa593d1ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index d323c5c23521f..e6e9c8a576af4 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-6.6/net-gro-don-t-merge-zcopy-skbs.patch b/queue-6.6/net-gro-don-t-merge-zcopy-skbs.patch new file mode 100644 index 0000000000..31a561c615 --- /dev/null +++ b/queue-6.6/net-gro-don-t-merge-zcopy-skbs.patch @@ -0,0 +1,48 @@ +From dd2477efe20311e898f97735727e7e3aa48c3493 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 22:44:42 +0200 +Subject: net: gro: don't merge zcopy skbs + +From: Sabrina Dubroca + +[ Upstream commit 4db79a322db8c97f7b73b8a347395ef4d685eb40 ] + +skb_gro_receive() can currently copy frags between the source and GRO +skb, without checking the zerocopy status, and in particular the +SKBFL_MANAGED_FRAG_REFS flag. + +When SKBFL_MANAGED_FRAG_REFS is set, the skb doesn't hold a reference +on the pages in shinfo->frags. Appending those frags to another skb's +frags without fixing up the page refcount can lead to UAF. + +When either the last skb in the GRO chain (the one we would append +frags to) or the source skb is zerocopy, don't merge the skbs. + +Fixes: 753f1ca4e1e5 ("net: introduce managed frags infrastructure") +Reported-by: Huzaifa Sidhpurwala +Signed-off-by: Sabrina Dubroca +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/gro.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/gro.c b/net/core/gro.c +index 0a9d4a3bb104d..1555e6bb5c9d7 100644 +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -110,6 +110,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) + if (p->pp_recycle != skb->pp_recycle) + return -ETOOMANYREFS; + ++ if (skb_zcopy(p) || skb_zcopy(skb)) ++ return -ETOOMANYREFS; ++ + if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) || + NAPI_GRO_CB(skb)->flush)) + return -E2BIG; +-- +2.53.0 + diff --git a/queue-6.6/net-lan966x-avoid-unregistering-netdev-on-register-f.patch b/queue-6.6/net-lan966x-avoid-unregistering-netdev-on-register-f.patch new file mode 100644 index 0000000000..d0fa1e9bc3 --- /dev/null +++ b/queue-6.6/net-lan966x-avoid-unregistering-netdev-on-register-f.patch @@ -0,0 +1,65 @@ +From a707d8c79d58bad38b637c479f0e97afa0f1747c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 21:43:11 +0900 +Subject: net: lan966x: avoid unregistering netdev on register failure + +From: Myeonghun Pak + +[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] + +lan966x_probe_port() stores the newly allocated net_device in the +port before calling register_netdev(). If register_netdev() fails, +the probe error path calls lan966x_cleanup_ports(), which sees +port->dev and calls unregister_netdev() for a device that was never +registered. + +Destroy the phylink instance created for this port and clear port->dev +before returning the registration error. The common cleanup path now skips +ports without port->dev before reaching the registered netdev cleanup, so +it only handles ports that reached the registered-netdev lifetime. + +This also avoids treating an uninitialized FDMA netdev and the failed port +as a NULL == NULL match in the common cleanup path. + +Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +index 5466f14e000ce..ce48685a50b20 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +@@ -750,11 +750,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; +- if (!port) ++ if (!port || !port->dev) + continue; + +- if (port->dev) +- unregister_netdev(port->dev); ++ unregister_netdev(port->dev); + + lan966x_xdp_port_deinit(port); + if (lan966x->fdma && lan966x->fdma_ndev == port->dev) +@@ -875,6 +874,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, + err = register_netdev(dev); + if (err) { + dev_err(lan966x->dev, "register_netdev failed\n"); ++ phylink_destroy(phylink); ++ port->phylink = NULL; ++ port->dev = NULL; + return err; + } + +-- +2.53.0 + diff --git a/queue-6.6/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-6.6/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..bbc9ef06e4 --- /dev/null +++ b/queue-6.6/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 311e9a3ce25db44fb48d2b3b6f6aa41d237bf68d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 148dda6570fc5..b5ec1250a674f 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -75,21 +75,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -218,6 +216,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -236,13 +235,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-6.6/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-6.6/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..db47a7f0a3 --- /dev/null +++ b/queue-6.6/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From 7568a9cc1adbc3a31dddbd054a134e69f570b504 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index b5ec1250a674f..1234c62fcbc7d 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -232,6 +232,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-6.6/net-mlx5-do-not-restore-destination-less-tc-rules.patch b/queue-6.6/net-mlx5-do-not-restore-destination-less-tc-rules.patch new file mode 100644 index 0000000000..ad790a9a17 --- /dev/null +++ b/queue-6.6/net-mlx5-do-not-restore-destination-less-tc-rules.patch @@ -0,0 +1,55 @@ +From c76036ecd485e6a653ea91b62182459206627774 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:33:02 +0300 +Subject: net/mlx5: Do not restore destination-less TC rules + +From: Jeroen Massar + +[ Upstream commit 8d0a5af8b1ba598e7340761729801624e7a9330e ] + +After IPsec policy/state TX rules are added, any TC flow rule, which +forwards packets to uplink, is modified to forward to IPsec TX tables. +As these tables are destroyed dynamically, whenever there is no +reference to them, the destinations of this kind of rules must be +restored to uplink, unless there is no destination for that rule. + +The flow rules FLOW_ACTION_ACCEPT, DROP, TRAP, GOTO and SAMPLE do not +have a destination port, and thus out_count = 0. + +At cleanup time of the rules in mlx5_esw_ipsec_modify_flow_dests +we call mlx5_eswitch_restore_ipsec_rule but as the above types +do not have a destination we get an underflow of out_count, as +the port is passed, which is esw_attr->out_count - 1. + +This change avoids calling mlx5_eswitch_restore_ipsec_rule when +there are no output destinations and thus avoids the underflow. + +Fixes: d1569537a837 ("net/mlx5e: Modify and restore TC rules for IPSec TX rules") +Signed-off-by: Jeroen Massar +Reviewed-by: Jianbo Liu +Reviewed-by: Cosmin Ratiu +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +index eed8fcde26138..7dfec1629bab7 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +@@ -129,7 +129,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, + + attr = flow->attr; + esw_attr = attr->esw_attr; +- if (esw_attr->out_count - esw_attr->split_count > 1) ++ if (!esw_attr->out_count || ++ esw_attr->out_count - esw_attr->split_count > 1) + return 0; + + err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, +-- +2.53.0 + diff --git a/queue-6.6/net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch b/queue-6.6/net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch new file mode 100644 index 0000000000..965f40c16d --- /dev/null +++ b/queue-6.6/net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch @@ -0,0 +1,192 @@ +From 06d2325a00e3b7045edf684e7b86ae5653716949 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 Dec 2023 06:41:43 +0100 +Subject: net: phy: c45: add genphy_c45_pma_read_ext_abilities() function + +From: Oleksij Rempel + +[ Upstream commit 0c476157085fe2ad13b9bec70ea672e86647fa1a ] + +Move part of the genphy_c45_pma_read_abilities() code to a separate +function. + +Some PHYs do not implement PMA/PMD status 2 register (Register 1.8) but +do implement PMA/PMD extended ability register (Register 1.11). To make +use of it, we need to be able to access this part of code separately. + +Signed-off-by: Oleksij Rempel +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/20231212054144.87527-2-o.rempel@pengutronix.de +Signed-off-by: Jakub Kicinski +Stable-dep-of: c78bdba7b966 ("net: phy: DP83TC811: add reading of abilities") +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phy-c45.c | 129 ++++++++++++++++++++++---------------- + include/linux/phy.h | 1 + + 2 files changed, 75 insertions(+), 55 deletions(-) + +diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c +index 8e6fd4962c486..747d14bf152c3 100644 +--- a/drivers/net/phy/phy-c45.c ++++ b/drivers/net/phy/phy-c45.c +@@ -919,6 +919,79 @@ int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev) + } + EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities); + ++/** ++ * genphy_c45_pma_read_ext_abilities - read supported link modes from PMA ++ * @phydev: target phy_device struct ++ * ++ * Read the supported link modes from the PMA/PMD extended ability register ++ * (Register 1.11). ++ */ ++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBLRM); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBKX4); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBKR); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_1000BT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_1000BKX); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_100BTX); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_100BTX); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10BT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10BT); ++ ++ if (val & MDIO_PMA_EXTABLE_NBT) { ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, ++ MDIO_PMA_NG_EXTABLE); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_NG_EXTABLE_2_5GBT); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_NG_EXTABLE_5GBT); ++ } ++ ++ if (val & MDIO_PMA_EXTABLE_BT1) { ++ val = genphy_c45_pma_baset1_read_abilities(phydev); ++ if (val < 0) ++ return val; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_pma_read_ext_abilities); ++ + /** + * genphy_c45_pma_read_abilities - read supported link modes from PMA + * @phydev: target phy_device struct +@@ -962,63 +1035,9 @@ int genphy_c45_pma_read_abilities(struct phy_device *phydev) + val & MDIO_PMA_STAT2_10GBER); + + if (val & MDIO_PMA_STAT2_EXTABLE) { +- val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); ++ val = genphy_c45_pma_read_ext_abilities(phydev); + if (val < 0) + return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBLRM); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBKX4); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBKR); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_1000BT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_1000BKX); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_100BTX); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_100BTX); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10BT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10BT); +- +- if (val & MDIO_PMA_EXTABLE_NBT) { +- val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, +- MDIO_PMA_NG_EXTABLE); +- if (val < 0) +- return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_NG_EXTABLE_2_5GBT); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_NG_EXTABLE_5GBT); +- } +- +- if (val & MDIO_PMA_EXTABLE_BT1) { +- val = genphy_c45_pma_baset1_read_abilities(phydev); +- if (val < 0) +- return val; +- } + } + + /* This is optional functionality. If not supported, we may get an error +diff --git a/include/linux/phy.h b/include/linux/phy.h +index a57e799b1de18..586973404ad47 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1857,6 +1857,7 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev); + int genphy_c45_an_disable_aneg(struct phy_device *phydev); + int genphy_c45_read_mdix(struct phy_device *phydev); + int genphy_c45_pma_read_abilities(struct phy_device *phydev); ++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev); + int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev); + int genphy_c45_read_eee_abilities(struct phy_device *phydev); + int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev); +-- +2.53.0 + diff --git a/queue-6.6/net-phy-dp83tc811-add-reading-of-abilities.patch b/queue-6.6/net-phy-dp83tc811-add-reading-of-abilities.patch new file mode 100644 index 0000000000..210376e96c --- /dev/null +++ b/queue-6.6/net-phy-dp83tc811-add-reading-of-abilities.patch @@ -0,0 +1,40 @@ +From 531bbd6e217e8db89be1cf59c833335eb26cbc24 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 09:19:47 +0200 +Subject: net: phy: DP83TC811: add reading of abilities + +From: Sven Schuchmann + +[ Upstream commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac ] + +At this time the driver is not listing any speeds +it supports. This should be ETHTOOL_LINK_MODE_100baseT1_Full_BIT +for DP83TC811. Add the missing call for phylib to read the abilities. + +Fixes: b753a9faaf9a ("net: phy: DP83TC811: Introduce support for the DP83TC811 phy") +Suggested-by: Andrew Lunn +Signed-off-by: Sven Schuchmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de +[pabeni@redhat.com: dropped revision history] +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/phy/dp83tc811.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c +index 7ea32fb77190c..5425a95352f9f 100644 +--- a/drivers/net/phy/dp83tc811.c ++++ b/drivers/net/phy/dp83tc811.c +@@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { + .config_init = dp83811_config_init, + .config_aneg = dp83811_config_aneg, + .soft_reset = dp83811_phy_reset, ++ .get_features = genphy_c45_pma_read_ext_abilities, + .get_wol = dp83811_get_wol, + .set_wol = dp83811_set_wol, + .config_intr = dp83811_config_intr, +-- +2.53.0 + diff --git a/queue-6.6/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch b/queue-6.6/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch new file mode 100644 index 0000000000..21305b909f --- /dev/null +++ b/queue-6.6/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch @@ -0,0 +1,65 @@ +From cad966469d4db2017ee09377e111853048a16a13 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 15:26:40 -0700 +Subject: net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint + +From: Xiang Mei + +[ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] + +The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and +smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: + + __string(name, smc->conn.lnk->ibname) + +conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on +these paths already handles this (e.g. !conn->lnk in +SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first +sendmsg()/recvmsg() on an SMC-D socket crashes: + + Oops: general protection fault, probably for non-canonical address + KASAN: null-ptr-deref in range [...] + RIP: 0010:strlen+0x1e/0xa0 + Call Trace: + trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) + smc_rx_recvmsg (net/smc/smc_rx.c:515) + smc_recvmsg (net/smc/af_smc.c:2859) + __sys_recvfrom (net/socket.c:2315) + __x64_sys_recvfrom (net/socket.c:2326) + do_syscall_64 + +The faulting address 0x3e0 is offsetof(struct smc_link, ibname), +confirming the NULL ->lnk deref. Enabling the tracepoint requires +root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has +no capability check, and SMC-D negotiation needs no admin step on +s390 or on x86 with the loopback ISM device loaded. + +Log an empty device name for SMC-D instead of dereferencing NULL. + +Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Dust Li +Reviewed-by: Sidraya Jayagond +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/smc/smc_tracepoint.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h +index 9fc5e586d24ab..380451912c4f1 100644 +--- a/net/smc/smc_tracepoint.h ++++ b/net/smc/smc_tracepoint.h +@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, + __field(const void *, smc) + __field(u64, net_cookie) + __field(size_t, len) +- __string(name, smc->conn.lnk->ibname) ++ __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") + ), + + TP_fast_assign( +-- +2.53.0 + diff --git a/queue-6.6/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch b/queue-6.6/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch new file mode 100644 index 0000000000..da1c871042 --- /dev/null +++ b/queue-6.6/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch @@ -0,0 +1,63 @@ +From 96f4883f70442fa673586bda55cd4499fcf99f77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:21:38 -0700 +Subject: net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot + +From: Xiang Mei + +[ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] + +On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is +reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() +populates V2 entries starting at index 1, so when no V1 device is +selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == +NULL and ism_chid[0] == 0. + +smc_v2_determine_accepted_chid() then matches the peer's CHID against +the array starting from index 0 using the CHID alone. A malicious +peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches +the empty slot, ini->ism_selected becomes 0, and the subsequent +ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at +offsetof(struct smcd_dev, lgr_lock) == 0x68: + + BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 + Write of size 4 at addr 0000000000000068 by task exploit/144 + Call Trace: + _raw_spin_lock_bh + smc_conn_create (net/smc/smc_core.c:1997) + __smc_connect (net/smc/af_smc.c:1447) + smc_connect (net/smc/af_smc.c:1720) + __sys_connect + __x64_sys_connect + do_syscall_64 + +Require ism_dev[i] to be non-NULL before accepting a CHID match. + +Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") +Reported-by: Weiming Shi +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/smc/af_smc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c +index 0e9a3b8da6a63..6629fd61be06a 100644 +--- a/net/smc/af_smc.c ++++ b/net/smc/af_smc.c +@@ -1378,7 +1378,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, + int i; + + for (i = 0; i < ini->ism_offered_cnt + 1; i++) { +- if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ++ if (ini->ism_dev[i] && ++ ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + ini->ism_selected = i; + return 0; + } +-- +2.53.0 + diff --git a/queue-6.6/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-6.6/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..3e686f377e --- /dev/null +++ b/queue-6.6/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From 91ce5d6e9891cc2412715200830c4c058c86f3ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 937aa78eed0e4..5ada6e54fb328 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -800,11 +800,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-6.6/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-6.6/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..e326bce9a6 --- /dev/null +++ b/queue-6.6/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From e0019cec602350baebce9c54e184961f1744d5a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 5ada6e54fb328..0c2e9724083ee 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -789,21 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-arptables-allow-xtables-nft-only-builds.patch b/queue-6.6/netfilter-arptables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..08a110acc6 --- /dev/null +++ b/queue-6.6/netfilter-arptables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,82 @@ +From 209d7fc49e05dc6bc6fa94dffaa381a99e7f5e78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 23 Jan 2024 16:42:48 +0100 +Subject: netfilter: arptables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 4654467dc7e111e84f43ed1b70322873ae77e7be ] + +Allows to build kernel that supports the arptables mangle target +via nftables' compat infra but without the arptables get/setsockopt +interface or the old arptables filter interpreter. + +IOW, setting IP_NF_ARPFILTER=n will break arptables-legacy, but +arptables-nft will continue to work as long as nftables compat +support is enabled. + +Signed-off-by: Florian Westphal +Reviewed-by: Phil Sutter +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index f71a7e9a7de6d..070475392236f 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -309,36 +309,34 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate "ARP tables support" +- select NETFILTER_XTABLES +- select NETFILTER_FAMILY_ARP +- depends on NETFILTER_ADVANCED +- help +- arptables is a general, extensible packet identification framework. +- The ARP packet filtering and mangling (manipulation)subsystems +- use this: say Y or M here if you want to use either of those. +- +- To compile it as a module, choose M here. If unsure, say N. ++ tristate + +-if IP_NF_ARPTABLES ++config NFT_COMPAT_ARP ++ tristate ++ depends on NF_TABLES_ARP && NFT_COMPAT ++ default m if NFT_COMPAT=m ++ default y if NFT_COMPAT=y + + config IP_NF_ARPFILTER +- tristate "ARP packet filtering" ++ tristate "arptables-legacy packet filtering support" ++ select IP_NF_ARPTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +- local output. On a bridge, you can also specify filtering rules +- for forwarded ARP packets. See the man page for arptables(8). ++ local output. This is only needed for arptables-legacy(8). ++ Neither arptables-nft nor nftables need this to work. + + To compile it as a module, choose M here. If unsure, say N. + + config IP_NF_ARP_MANGLE + tristate "ARP payload mangling" ++ depends on IP_NF_ARPTABLES || NFT_COMPAT_ARP + help + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + +-endif # IP_NF_ARPTABLES ++ This option is needed by both arptables-legacy and arptables-nft. ++ It is not used by nftables. + + endmenu + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-arptables-select-netfilter_family_arp-when.patch b/queue-6.6/netfilter-arptables-select-netfilter_family_arp-when.patch new file mode 100644 index 0000000000..79869b208f --- /dev/null +++ b/queue-6.6/netfilter-arptables-select-netfilter_family_arp-when.patch @@ -0,0 +1,112 @@ +From 9cd2a62d96f43ca483d588ac324dad0e31a152c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Mar 2024 21:15:52 -0700 +Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building + arp_tables.c + +From: Kuniyuki Iwashima + +[ Upstream commit 15fba562f7a9f04322b8bfc8f392e04bb93d81be ] + +syzkaller started to report a warning below [0] after consuming the +commit 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only +builds"). + +The change accidentally removed the dependency on NETFILTER_FAMILY_ARP +from IP_NF_ARPTABLES. + +If NF_TABLES_ARP is not enabled on Kconfig, NETFILTER_FAMILY_ARP will +be removed and some code necessary for arptables will not be compiled. + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + CONFIG_NETFILTER_FAMILY_ARP=y + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + + $ make olddefconfig + + $ grep -E "(NETFILTER_FAMILY_ARP|IP_NF_ARPTABLES|NF_TABLES_ARP)" .config + # CONFIG_NF_TABLES_ARP is not set + CONFIG_IP_NF_ARPTABLES=y + +So, when nf_register_net_hooks() is called for arptables, it will +trigger the splat below. + +Now IP_NF_ARPTABLES is only enabled by IP_NF_ARPFILTER, so let's +restore the dependency on NETFILTER_FAMILY_ARP in IP_NF_ARPFILTER. + +[0]: +WARNING: CPU: 0 PID: 242 at net/netfilter/core.c:316 nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Modules linked in: +CPU: 0 PID: 242 Comm: syz-executor.0 Not tainted 6.8.0-12821-g537c2e91d354 #10 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 +RIP: 0010:nf_hook_entry_head+0x1e1/0x2c0 net/netfilter/core.c:316 +Code: 83 fd 04 0f 87 bc 00 00 00 e8 5b 84 83 fd 4d 8d ac ec a8 0b 00 00 e8 4e 84 83 fd 4c 89 e8 5b 5d 41 5c 41 5d c3 e8 3f 84 83 fd <0f> 0b e8 38 84 83 fd 45 31 ed 5b 5d 4c 89 e8 41 5c 41 5d c3 e8 26 +RSP: 0018:ffffc90000b8f6e8 EFLAGS: 00010293 +RAX: 0000000000000000 RBX: 0000000000000003 RCX: ffffffff83c42164 +RDX: ffff888106851180 RSI: ffffffff83c42321 RDI: 0000000000000005 +RBP: 0000000000000000 R08: 0000000000000005 R09: 000000000000000a +R10: 0000000000000003 R11: ffff8881055c2f00 R12: ffff888112b78000 +R13: 0000000000000000 R14: ffff8881055c2f00 R15: ffff8881055c2f00 +FS: 00007f377bd78800(0000) GS:ffff88811b000000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 0000000000496068 CR3: 000000011298b003 CR4: 0000000000770ef0 +PKRU: 55555554 +Call Trace: + + __nf_register_net_hook+0xcd/0x7a0 net/netfilter/core.c:428 + nf_register_net_hook+0x116/0x170 net/netfilter/core.c:578 + nf_register_net_hooks+0x5d/0xc0 net/netfilter/core.c:594 + arpt_register_table+0x250/0x420 net/ipv4/netfilter/arp_tables.c:1553 + arptable_filter_table_init+0x41/0x60 net/ipv4/netfilter/arptable_filter.c:39 + xt_find_table_lock+0x2e9/0x4b0 net/netfilter/x_tables.c:1260 + xt_request_find_table_lock+0x2b/0xe0 net/netfilter/x_tables.c:1285 + get_info+0x169/0x5c0 net/ipv4/netfilter/arp_tables.c:808 + do_arpt_get_ctl+0x3f9/0x830 net/ipv4/netfilter/arp_tables.c:1444 + nf_getsockopt+0x76/0xd0 net/netfilter/nf_sockopt.c:116 + ip_getsockopt+0x17d/0x1c0 net/ipv4/ip_sockglue.c:1777 + tcp_getsockopt+0x99/0x100 net/ipv4/tcp.c:4373 + do_sock_getsockopt+0x279/0x360 net/socket.c:2373 + __sys_getsockopt+0x115/0x1e0 net/socket.c:2402 + __do_sys_getsockopt net/socket.c:2412 [inline] + __se_sys_getsockopt net/socket.c:2409 [inline] + __x64_sys_getsockopt+0xbd/0x150 net/socket.c:2409 + do_syscall_x64 arch/x86/entry/common.c:52 [inline] + do_syscall_64+0x4f/0x110 arch/x86/entry/common.c:83 + entry_SYSCALL_64_after_hwframe+0x46/0x4e +RIP: 0033:0x7f377beca6fe +Code: 1f 44 00 00 48 8b 15 01 97 0a 00 f7 d8 64 89 02 b8 ff ff ff ff eb b8 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 37 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 c9 +RSP: 002b:00000000005df728 EFLAGS: 00000246 ORIG_RAX: 0000000000000037 +RAX: ffffffffffffffda RBX: 00000000004966e0 RCX: 00007f377beca6fe +RDX: 0000000000000060 RSI: 0000000000000000 RDI: 0000000000000003 +RBP: 000000000042938a R08: 00000000005df73c R09: 00000000005df800 +R10: 00000000004966e8 R11: 0000000000000246 R12: 0000000000000003 +R13: 0000000000496068 R14: 0000000000000003 R15: 00000000004bc9d8 + + +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Reported-by: syzkaller +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Simon Horman +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 8f6e950163a79..1b991b889506a 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -329,6 +329,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ select NETFILTER_FAMILY_ARP + depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of +-- +2.53.0 + diff --git a/queue-6.6/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-6.6/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..09a4dec954 --- /dev/null +++ b/queue-6.6/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From 66369db872908fe432e1d407449372db0834fd98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index ec286e54229b7..ca426e49ea1a1 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-ebtables-allow-xtables-nft-only-builds.patch b/queue-6.6/netfilter-ebtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..eeb0f97deb --- /dev/null +++ b/queue-6.6/netfilter-ebtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,84 @@ +From 812f90f09db00caf502621c1b511708e54f7326b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:12 +0100 +Subject: netfilter: ebtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit 7ad269787b6615ca56bb161063331991fce51abf ] + +Same patch as previous one, but for ebtables. + +To build a kernel that only supports ebtables-nft, the builtin tables +need to be disabled, i.e.: + +CONFIG_BRIDGE_EBT_BROUTE=n +CONFIG_BRIDGE_EBT_T_FILTER=n +CONFIG_BRIDGE_EBT_T_NAT=n + +The ebtables specific extensions can then be used nftables' +NFT_COMPAT interface. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 7 +++++++ + net/bridge/netfilter/Makefile | 2 +- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 7f304a19ac1bf..104c0125e32e8 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -39,6 +39,10 @@ config NF_CONNTRACK_BRIDGE + + To compile it as a module, choose M here. If unsure, say N. + ++# old sockopt interface and eval loop ++config BRIDGE_NF_EBTABLES_LEGACY ++ tristate ++ + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" + depends on BRIDGE && NETFILTER && NETFILTER_XTABLES +@@ -55,6 +59,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -65,6 +70,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -74,6 +80,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" ++ select BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile +index 1c9ce49ab6513..b9a1303da9771 100644 +--- a/net/bridge/netfilter/Makefile ++++ b/net/bridge/netfilter/Makefile +@@ -9,7 +9,7 @@ obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o + # connection tracking + obj-$(CONFIG_NF_CONNTRACK_BRIDGE) += nf_conntrack_bridge.o + +-obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o ++obj-$(CONFIG_BRIDGE_NF_EBTABLES_LEGACY) += ebtables.o + + # tables + obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o +-- +2.53.0 + diff --git a/queue-6.6/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-6.6/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..4cf9e2f9eb --- /dev/null +++ b/queue-6.6/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From 1f8996773559b470b18aa8d370e630503e096a72 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 33d8640d21ac1..43c808e525e87 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index fdb988c24916a..f76d45dfe9b46 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 8b981b2041b5d..af0732e2f889d 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-6.6/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-6.6/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..3142019308 --- /dev/null +++ b/queue-6.6/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From 8537307f1432dc229dd72bbba23abf7139c20bbf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 8f19253024b0a..33d8640d21ac1 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 278f324e67524..fdb988c24916a 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 9066f7f376d57..8b981b2041b5d 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index f99e348c8f37f..ec286e54229b7 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-6.6/netfilter-exclude-legacy-tables-on-preempt_rt.patch b/queue-6.6/netfilter-exclude-legacy-tables-on-preempt_rt.patch new file mode 100644 index 0000000000..b74349650b --- /dev/null +++ b/queue-6.6/netfilter-exclude-legacy-tables-on-preempt_rt.patch @@ -0,0 +1,335 @@ +From 2bf5b7722bf6803b23acd83a7421e9eba760e47a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Jun 2025 17:44:23 +0200 +Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT. + +From: Pablo Neira Ayuso + +[ Upstream commit 9fce66583f06c212e95e4b76dd61d8432ffa56b6 ] + +The seqcount xt_recseq is used to synchronize the replacement of +xt_table::private in xt_replace_table() against all readers such as +ipt_do_table() + +To ensure that there is only one writer, the writing side disables +bottom halves. The sequence counter can be acquired recursively. Only the +first invocation modifies the sequence counter (signaling that a writer +is in progress) while the following (recursive) writer does not modify +the counter. +The lack of a proper locking mechanism for the sequence counter can lead +to live lock on PREEMPT_RT if the high prior reader preempts the +writer. Additionally if the per-CPU lock on PREEMPT_RT is removed from +local_bh_disable() then there is no synchronisation for the per-CPU +sequence counter. + +The affected code is "just" the legacy netfilter code which is replaced +by "netfilter tables". That code can be disabled without sacrificing +functionality because everything is provided by the newer +implementation. This will only requires the usage of the "-nft" tools +instead of the "-legacy" ones. +The long term plan is to remove the legacy code so lets accelerate the +progress. + +Relax dependencies on iptables legacy, replace select with depends on, +this should cause no harm to existing kernel configs and users can still +toggle IP{6}_NF_IPTABLES_LEGACY in any case. +Make EBTABLES_LEGACY, IPTABLES_LEGACY and ARPTABLES depend on +NETFILTER_XTABLES_LEGACY. Hide xt_recseq and its users, +xt_register_table() and xt_percpu_counter_alloc() behind +NETFILTER_XTABLES_LEGACY. Let NETFILTER_XTABLES_LEGACY depend on +!PREEMPT_RT. + +This will break selftest expecing the legacy options enabled and will be +addressed in a following patch. + +Co-developed-by: Florian Westphal +Co-developed-by: Sebastian Andrzej Siewior +Signed-off-by: Sebastian Andrzej Siewior +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 10 +++++----- + net/ipv4/netfilter/Kconfig | 24 ++++++++++++------------ + net/ipv6/netfilter/Kconfig | 19 +++++++++---------- + net/netfilter/Kconfig | 10 ++++++++++ + net/netfilter/x_tables.c | 16 +++++++++++----- + 5 files changed, 47 insertions(+), 32 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index f16bbbbb94817..60f28e4fb5c0a 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -42,8 +42,8 @@ config NF_CONNTRACK_BRIDGE + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY + tristate "Legacy EBTABLES support" +- depends on BRIDGE && NETFILTER_XTABLES +- default n ++ depends on BRIDGE && NETFILTER_XTABLES_LEGACY ++ default n + help + Legacy ebtables packet/frame classifier. + This is not needed if you are using ebtables over nftables +@@ -65,7 +65,7 @@ if BRIDGE_NF_EBTABLES + # + config BRIDGE_EBT_BROUTE + tristate "ebt: broute table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables broute table is used to define rules that decide between + bridging and routing frames, giving Linux the functionality of a +@@ -76,7 +76,7 @@ config BRIDGE_EBT_BROUTE + + config BRIDGE_EBT_T_FILTER + tristate "ebt: filter table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables filter table is used to define frame filtering rules at + local input, forwarding and local output. See the man page for +@@ -86,7 +86,7 @@ config BRIDGE_EBT_T_FILTER + + config BRIDGE_EBT_T_NAT + tristate "ebt: nat table support" +- select BRIDGE_NF_EBTABLES_LEGACY ++ depends on BRIDGE_NF_EBTABLES_LEGACY + help + The ebtables nat table is used to define rules that alter the MAC + source address (MAC SNAT) or the MAC destination address (MAC DNAT). +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index ef8009281da5c..2c438b140e88f 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -13,8 +13,8 @@ config NF_DEFRAG_IPV4 + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY + tristate "Legacy IP tables support" +- default n +- select NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + iptables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -182,8 +182,8 @@ config IP_NF_MATCH_TTL + # `filter', generic and specific targets + config IP_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -220,10 +220,10 @@ config IP_NF_TARGET_SYNPROXY + config IP_NF_NAT + tristate "iptables NAT support" + depends on NF_CONNTRACK ++ depends on IP_NF_IPTABLES_LEGACY + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -263,8 +263,8 @@ endif # IP_NF_NAT + # mangle + specific targets + config IP_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -299,7 +299,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -313,7 +313,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP_NF_IPTABLES_LEGACY ++ depends on IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -325,8 +325,8 @@ endif # IP_NF_IPTABLES + # ARP tables + config IP_NF_ARPTABLES + tristate "Legacy ARPTABLES support" +- depends on NETFILTER_XTABLES +- default n ++ depends on NETFILTER_XTABLES_LEGACY ++ default n + help + arptables is a legacy packet classifier. + This is not needed if you are using arptables over nftables +@@ -342,7 +342,7 @@ config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES + select NETFILTER_FAMILY_ARP +- depends on NETFILTER_XTABLES ++ depends on NETFILTER_XTABLES_LEGACY + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index e087a8e97ba78..276860f65baae 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -9,9 +9,8 @@ menu "IPv6: Netfilter Configuration" + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY + tristate "Legacy IP6 tables support" +- depends on INET && IPV6 +- select NETFILTER_XTABLES +- default n ++ depends on INET && IPV6 && NETFILTER_XTABLES_LEGACY ++ default m if NETFILTER_XTABLES_LEGACY + help + ip6tables is a legacy packet classifier. + This is not needed if you are using iptables over nftables +@@ -196,8 +195,8 @@ config IP6_NF_TARGET_HL + + config IP6_NF_FILTER + tristate "Packet filtering" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + tristate + help + Packet filtering defines a table `filter', which has a series of +@@ -233,8 +232,8 @@ config IP6_NF_TARGET_SYNPROXY + + config IP6_NF_MANGLE + tristate "Packet mangling" +- default m if NETFILTER_ADVANCED=n +- select IP6_NF_IPTABLES_LEGACY ++ default m if NETFILTER_ADVANCED=n || IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -244,7 +243,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -258,7 +257,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED +- select IP6_NF_IPTABLES_LEGACY ++ depends on IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -269,8 +268,8 @@ config IP6_NF_NAT + tristate "ip6tables NAT support" + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED ++ depends on IP6_NF_IPTABLES_LEGACY + select NF_NAT +- select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index df2dc21304efb..0d1d997abe191 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -762,6 +762,16 @@ config NETFILTER_XTABLES_COMPAT + + If unsure, say N. + ++config NETFILTER_XTABLES_LEGACY ++ bool "Netfilter legacy tables support" ++ depends on !PREEMPT_RT ++ help ++ Say Y here if you still require support for legacy tables. This is ++ required by the legacy tools (iptables-legacy) and is not needed if ++ you use iptables over nftables (iptables-nft). ++ Legacy support is not limited to IP, it also includes EBTABLES and ++ ARPTABLES. ++ + comment "Xtables combined modules" + + config NETFILTER_XT_MARK +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index c1ab85fb8c46d..98384bb17bbe3 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1339,12 +1339,13 @@ void xt_compat_unlock(u_int8_t af) + EXPORT_SYMBOL_GPL(xt_compat_unlock); + #endif + +-DEFINE_PER_CPU(seqcount_t, xt_recseq); +-EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); +- + struct static_key xt_tee_enabled __read_mostly; + EXPORT_SYMBOL_GPL(xt_tee_enabled); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY ++DEFINE_PER_CPU(seqcount_t, xt_recseq); ++EXPORT_PER_CPU_SYMBOL_GPL(xt_recseq); ++ + static int xt_jumpstack_alloc(struct xt_table_info *i) + { + unsigned int size; +@@ -1536,6 +1537,7 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++#endif + + #ifdef CONFIG_PROC_FS + static void *xt_table_seq_start(struct seq_file *seq, loff_t *pos) +@@ -1919,6 +1921,7 @@ void xt_proto_fini(struct net *net, u_int8_t af) + } + EXPORT_SYMBOL_GPL(xt_proto_fini); + ++#ifdef CONFIG_NETFILTER_XTABLES_LEGACY + /** + * xt_percpu_counter_alloc - allocate x_tables rule counter + * +@@ -1973,6 +1976,7 @@ void xt_percpu_counter_free(struct xt_counters *counters) + free_percpu((void __percpu *)pcnt); + } + EXPORT_SYMBOL_GPL(xt_percpu_counter_free); ++#endif + + static int __net_init xt_net_init(struct net *net) + { +@@ -2005,8 +2009,10 @@ static int __init xt_init(void) + unsigned int i; + int rv; + +- for_each_possible_cpu(i) { +- seqcount_init(&per_cpu(xt_recseq, i)); ++ if (IS_ENABLED(CONFIG_NETFILTER_XTABLES_LEGACY)) { ++ for_each_possible_cpu(i) { ++ seqcount_init(&per_cpu(xt_recseq, i)); ++ } + } + + xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.6/netfilter-make-legacy-configs-user-selectable.patch b/queue-6.6/netfilter-make-legacy-configs-user-selectable.patch new file mode 100644 index 0000000000..a96987d733 --- /dev/null +++ b/queue-6.6/netfilter-make-legacy-configs-user-selectable.patch @@ -0,0 +1,104 @@ +From b8effb0ad3a9f6fff1b2b97e499104c7b3cbcd34 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Sep 2024 02:58:54 -0700 +Subject: netfilter: Make legacy configs user selectable + +From: Breno Leitao + +[ Upstream commit 6c959fd5e17387201dba3619b2e6af213939a0a7 ] + +This option makes legacy Netfilter Kconfig user selectable, giving users +the option to configure iptables without enabling any other config. + +Make the following KConfig entries user selectable: + * BRIDGE_NF_EBTABLES_LEGACY + * IP_NF_ARPTABLES + * IP_NF_IPTABLES_LEGACY + * IP6_NF_IPTABLES_LEGACY + +Signed-off-by: Breno Leitao +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/Kconfig | 8 +++++++- + net/ipv4/netfilter/Kconfig | 16 ++++++++++++++-- + net/ipv6/netfilter/Kconfig | 9 ++++++++- + 3 files changed, 29 insertions(+), 4 deletions(-) + +diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig +index 104c0125e32e8..f16bbbbb94817 100644 +--- a/net/bridge/netfilter/Kconfig ++++ b/net/bridge/netfilter/Kconfig +@@ -41,7 +41,13 @@ config NF_CONNTRACK_BRIDGE + + # old sockopt interface and eval loop + config BRIDGE_NF_EBTABLES_LEGACY +- tristate ++ tristate "Legacy EBTABLES support" ++ depends on BRIDGE && NETFILTER_XTABLES ++ default n ++ help ++ Legacy ebtables packet/frame classifier. ++ This is not needed if you are using ebtables over nftables ++ (iptables-nft). + + menuconfig BRIDGE_NF_EBTABLES + tristate "Ethernet Bridge tables (ebtables) support" +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 1b991b889506a..ef8009281da5c 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -12,7 +12,13 @@ config NF_DEFRAG_IPV4 + + # old sockopt interface and eval loop + config IP_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP tables support" ++ default n ++ select NETFILTER_XTABLES ++ help ++ iptables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" +@@ -318,7 +324,13 @@ endif # IP_NF_IPTABLES + + # ARP tables + config IP_NF_ARPTABLES +- tristate ++ tristate "Legacy ARPTABLES support" ++ depends on NETFILTER_XTABLES ++ default n ++ help ++ arptables is a legacy packet classifier. ++ This is not needed if you are using arptables over nftables ++ (iptables-nft). + + config NFT_COMPAT_ARP + tristate +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index f3c8e2d918e13..e087a8e97ba78 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -8,7 +8,14 @@ menu "IPv6: Netfilter Configuration" + + # old sockopt interface and eval loop + config IP6_NF_IPTABLES_LEGACY +- tristate ++ tristate "Legacy IP6 tables support" ++ depends on INET && IPV6 ++ select NETFILTER_XTABLES ++ default n ++ help ++ ip6tables is a legacy packet classifier. ++ This is not needed if you are using iptables over nftables ++ (iptables-nft). + + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" +-- +2.53.0 + diff --git a/queue-6.6/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-6.6/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..202c9d7cb8 --- /dev/null +++ b/queue-6.6/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From 76190268d1575a7cf36ebb0f39ebec01c83d384f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 5897f3dbaf7c3..df2022fe440b0 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -310,6 +310,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 132b0e4a6d4df..13593391d6058 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 8b8885a73c764..c6d5b927830dd 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 564054123772a..9b905c6562313 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index fe89a056eb06c..8240b3b0e0260 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1789,14 +1789,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1887,7 +1879,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index c03c1a4ea7cab..fb85745793ba5 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 6a51e61b35562..6259bcf178bba 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -95,7 +95,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 12ca666d6e2c1..ca6964b957ead 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 33330e13ea18d..c7b91b2042dc6 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 2b89adc1e5751..81175c20ccbe8 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index 131f7bb2110d3..c956c2bd73d59 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1795,14 +1795,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1894,7 +1886,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 16a38d56b2e54..982900920e730 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 39f0716667131..475361aa81310 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -88,7 +88,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 52d597b16b658..bef2d309369bc 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 01def8aa7a2e8..a99879f173b4a 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 66018b169b010..c44834d93fc79 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 98384bb17bbe3..670483735d225 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1537,6 +1537,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-6.6/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-6.6/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..4aea62f020 --- /dev/null +++ b/queue-6.6/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From 47c134bacc6253723f9853513c1cf61bd6d0060a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index df2022fe440b0..706f08839050a 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -309,8 +309,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 9b905c6562313..f9dd18244f251 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 8240b3b0e0260..02730b6ab8203 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1791,7 +1790,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index ca6964b957ead..87d934b12bcb6 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index c956c2bd73d59..2cbf346940d29 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1797,7 +1796,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index bef2d309369bc..cf260d8ebdb70 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 670483735d225..593eb3ebef128 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1521,23 +1524,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1547,6 +1533,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1556,6 +1550,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1566,6 +1561,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2012,8 +2051,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2022,8 +2063,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-6.6/netfilter-x_tables-close-dangling-table-module-init-.patch b/queue-6.6/netfilter-x_tables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..5fffcfcb32 --- /dev/null +++ b/queue-6.6/netfilter-x_tables-close-dangling-table-module-init-.patch @@ -0,0 +1,406 @@ +From 13a37f515950e43f29e085dea0ca998b8925b9d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:20 +0200 +Subject: netfilter: x_tables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] + +Similar to the previous ebtables patch: +template add exposes the table to userspace, we must do this last to +rnsure the pernet ops are set up (contain the destructors). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ + net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- + net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- + net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- + net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- + 9 files changed, 105 insertions(+), 99 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 382345567a600..370b635e3523b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { + + static int __init arptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- arptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); +- if (IS_ERR(arpfilter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(arpfilter_ops)) + return PTR_ERR(arpfilter_ops); +- } + + ret = register_pernet_subsys(&arptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ arptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(arpfilter_ops); +- return ret; ++ unregister_pernet_subsys(&arptable_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(arpfilter_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index fb85745793ba5..409e96c72164b 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { + + static int __init iptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- iptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&iptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ iptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_filter_net_ops); ++ goto err_free; + } + + return 0; ++err_free: ++ kfree(filter_ops); ++ return ret; + } + + static void __exit iptable_filter_fini(void) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 6259bcf178bba..b8618bdf5fdc4 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -110,25 +110,26 @@ static struct pernet_operations iptable_mangle_net_ops = { + + static int __init iptable_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- iptable_mangle_table_init); +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); +- ret = PTR_ERR(mangle_ops); +- return ret; +- } ++ if (IS_ERR(mangle_ops)) ++ return PTR_ERR(mangle_ops); + + ret = register_pernet_subsys(&iptable_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ iptable_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index c7b91b2042dc6..94ad7fad3a1f3 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, +- iptable_raw_table_init); +- if (ret < 0) +- return ret; +- + rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&iptable_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ++ iptable_raw_table_init); + if (ret < 0) { +- xt_unregister_template(table); +- kfree(rawtable_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 81175c20ccbe8..491894511c544 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { + + static int __init iptable_security_init(void) + { +- int ret = xt_register_template(&security_table, +- iptable_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&iptable_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ iptable_security_table_init); + if (ret < 0) { +- xt_unregister_template(&security_table); +- kfree(sectbl_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 982900920e730..f444071346859 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { + + static int __init ip6table_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- ip6table_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&ip6table_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ip6table_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(filter_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 475361aa81310..dbc64e4428403 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -103,25 +103,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { + + static int __init ip6table_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- ip6table_mangle_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); ++ if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); +- } + + ret = register_pernet_subsys(&ip6table_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ ip6table_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index a99879f173b4a..1eadf553c746e 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, ip6table_raw_table_init); +- if (ret < 0) +- return ret; +- + /* Register hooks */ + rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&ip6table_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ip6table_raw_table_init); + if (ret < 0) { +- kfree(rawtable_ops); +- xt_unregister_template(table); +- return ret; ++ unregister_pernet_subsys(&ip6table_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index c44834d93fc79..4bd5d97b8ab65 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { + + static int __init ip6table_security_init(void) + { +- int ret = xt_register_template(&security_table, +- ip6table_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&ip6table_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ ip6table_security_table_init); + if (ret < 0) { +- kfree(sectbl_ops); +- xt_unregister_template(&security_table); +- return ret; ++ unregister_pernet_subsys(&ip6table_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-x_tables-unregister-the-templates-first.patch b/queue-6.6/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..bed0998f7b --- /dev/null +++ b/queue-6.6/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From d0ee182026590ec13c3c149da649f4e7751e7ea4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 78cd5ee24448f..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index b9062f4552ace..c03c1a4ea7cab 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 3abb430af9e6f..6a51e61b35562 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -134,8 +134,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index ca5e5b21587cd..33330e13ea18d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index d885443cb2679..2b89adc1e5751 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index df785ebda0ca4..16a38d56b2e54 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index a88b2ce4a3cb8..39f0716667131 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -127,8 +127,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 08861d5d1f4db..01def8aa7a2e8 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 4df14a9bae782..66018b169b010 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-xtables-allow-xtables-nft-only-builds.patch b/queue-6.6/netfilter-xtables-allow-xtables-nft-only-builds.patch new file mode 100644 index 0000000000..5b4747d666 --- /dev/null +++ b/queue-6.6/netfilter-xtables-allow-xtables-nft-only-builds.patch @@ -0,0 +1,315 @@ +From 67aec5a1e1f22a9082cf7f661a3163bae38a147b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Jan 2024 10:21:11 +0100 +Subject: netfilter: xtables: allow xtables-nft only builds + +From: Florian Westphal + +[ Upstream commit a9525c7f6219cee9284c0031c5930e8d41384677 ] + +Add hidden IP(6)_NF_IPTABLES_LEGACY symbol. + +When any of the "old" builtin tables are enabled the "old" iptables +interface will be supported. + +To disable the old set/getsockopt interface the existing options +for the builtin tables need to be turned off: + +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER is not set +CONFIG_IP_NF_NAT is not set +CONFIG_IP_NF_MANGLE is not set +CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_SECURITY is not set + +Same for CONFIG_IP6_NF_ variants. + +This allows to build a kernel that only supports ip(6)tables-nft +(iptables-over-nftables api). + +In the future the _LEGACY symbol will become visible and the select +statements will be turned into 'depends on', but for now be on safe side +so "make oldconfig" won't break things. + +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 15 ++++++++++++--- + net/ipv4/netfilter/Makefile | 2 +- + net/ipv6/netfilter/Kconfig | 20 ++++++++++++++------ + net/ipv6/netfilter/Makefile | 2 +- + net/netfilter/Kconfig | 12 ++++++------ + 5 files changed, 34 insertions(+), 17 deletions(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 070475392236f..7835230872818 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -10,6 +10,10 @@ config NF_DEFRAG_IPV4 + tristate + default n + ++# old sockopt interface and eval loop ++config IP_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" + help +@@ -152,7 +156,7 @@ config IP_NF_MATCH_ECN + config IP_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP_NF_MANGLE || IP_NF_RAW ++ depends on IP_NF_MANGLE || IP_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -173,6 +177,7 @@ config IP_NF_MATCH_TTL + config IP_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -182,7 +187,7 @@ config IP_NF_FILTER + + config IP_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP_NF_FILTER ++ depends on IP_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV4 + default m if NETFILTER_ADVANCED=n + help +@@ -212,6 +217,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT ++ select IP6_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -252,6 +258,7 @@ endif # IP_NF_NAT + config IP_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -261,7 +268,7 @@ config IP_NF_MANGLE + + config IP_NF_TARGET_ECN + tristate "ECN target support" +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `ECN' target, which can be used in the iptables mangle +@@ -286,6 +293,7 @@ config IP_NF_TARGET_TTL + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -299,6 +307,7 @@ config IP_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile +index 5a26f9de1ab92..85502d4dfbb4d 100644 +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -25,7 +25,7 @@ obj-$(CONFIG_NFT_FIB_IPV4) += nft_fib_ipv4.o + obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o + + # generic IP tables +-obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o ++obj-$(CONFIG_IP_NF_IPTABLES_LEGACY) += ip_tables.o + + # the three instances of ip_tables + obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o +diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig +index 0ba62f4868f97..f3c8e2d918e13 100644 +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -6,6 +6,10 @@ + menu "IPv6: Netfilter Configuration" + depends on INET && IPV6 && NETFILTER + ++# old sockopt interface and eval loop ++config IP6_NF_IPTABLES_LEGACY ++ tristate ++ + config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" + help +@@ -147,7 +151,7 @@ config IP6_NF_MATCH_MH + config IP6_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED +- depends on IP6_NF_MANGLE || IP6_NF_RAW ++ depends on IP6_NF_MANGLE || IP6_NF_RAW || NFT_COMPAT + help + This option allows you to match packets whose replies would + go out via the interface the packet came in. +@@ -186,6 +190,8 @@ config IP6_NF_TARGET_HL + config IP6_NF_FILTER + tristate "Packet filtering" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY ++ tristate + help + Packet filtering defines a table `filter', which has a series of + rules for simple packet filtering at local input, forwarding and +@@ -195,7 +201,7 @@ config IP6_NF_FILTER + + config IP6_NF_TARGET_REJECT + tristate "REJECT target support" +- depends on IP6_NF_FILTER ++ depends on IP6_NF_FILTER || NFT_COMPAT + select NF_REJECT_IPV6 + default m if NETFILTER_ADVANCED=n + help +@@ -221,6 +227,7 @@ config IP6_NF_TARGET_SYNPROXY + config IP6_NF_MANGLE + tristate "Packet mangling" + default m if NETFILTER_ADVANCED=n ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `mangle' table to iptables: see the man page for + iptables(8). This table is used for various packet alterations +@@ -230,6 +237,7 @@ config IP6_NF_MANGLE + + config IP6_NF_RAW + tristate 'raw table support (required for TRACE)' ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `raw' table to ip6tables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING +@@ -243,6 +251,7 @@ config IP6_NF_SECURITY + tristate "Security table" + depends on SECURITY + depends on NETFILTER_ADVANCED ++ select IP6_NF_IPTABLES_LEGACY + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. +@@ -254,6 +263,7 @@ config IP6_NF_NAT + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED + select NF_NAT ++ select IP6_NF_IPTABLES_LEGACY + select NETFILTER_XT_NAT + help + This enables the `nat' table in ip6tables. This allows masquerading, +@@ -262,25 +272,23 @@ config IP6_NF_NAT + + To compile it as a module, choose M here. If unsure, say N. + +-if IP6_NF_NAT +- + config IP6_NF_TARGET_MASQUERADE + tristate "MASQUERADE target support" + select NETFILTER_XT_TARGET_MASQUERADE ++ depends on IP6_NF_NAT + help + This is a backwards-compat option for the user's convenience + (e.g. when running oldconfig). It selects NETFILTER_XT_TARGET_MASQUERADE. + + config IP6_NF_TARGET_NPT + tristate "NPT (Network Prefix translation) target support" ++ depends on IP6_NF_NAT || NFT_COMPAT + help + This option adds the `SNPT' and `DNPT' target, which perform + stateless IPv6-to-IPv6 Network Prefix Translation per RFC 6296. + + To compile it as a module, choose M here. If unsure, say N. + +-endif # IP6_NF_NAT +- + endif # IP6_NF_IPTABLES + endmenu + +diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile +index b8d6dc9aeeb6f..66ce6fa5b2f52 100644 +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -4,7 +4,7 @@ + # + + # Link order matters here. +-obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o ++obj-$(CONFIG_IP6_NF_IPTABLES_LEGACY) += ip6_tables.o + obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o + obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index 441d1f1341100..df2dc21304efb 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -818,7 +818,7 @@ config NETFILTER_XT_TARGET_AUDIT + + config NETFILTER_XT_TARGET_CHECKSUM + tristate "CHECKSUM target support" +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `CHECKSUM' target, which can be used in the iptables mangle +@@ -869,7 +869,7 @@ config NETFILTER_XT_TARGET_CONNSECMARK + config NETFILTER_XT_TARGET_CT + tristate '"CT" target support' + depends on NF_CONNTRACK +- depends on IP_NF_RAW || IP6_NF_RAW ++ depends on IP_NF_RAW || IP6_NF_RAW || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This options adds a `CT' target, which allows to specify initial +@@ -880,7 +880,7 @@ config NETFILTER_XT_TARGET_CT + + config NETFILTER_XT_TARGET_DSCP + tristate '"DSCP" and "TOS" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a `DSCP' target, which allows you to manipulate +@@ -896,7 +896,7 @@ config NETFILTER_XT_TARGET_DSCP + + config NETFILTER_XT_TARGET_HL + tristate '"HL" hoplimit target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds the "HL" (for IPv6) and "TTL" (for IPv4) +@@ -1080,7 +1080,7 @@ config NETFILTER_XT_TARGET_TPROXY + depends on NETFILTER_ADVANCED + depends on IPV6 || IPV6=n + depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n +- depends on IP_NF_MANGLE ++ depends on IP_NF_MANGLE || NFT_COMPAT + select NF_DEFRAG_IPV4 + select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n + select NF_TPROXY_IPV4 +@@ -1147,7 +1147,7 @@ config NETFILTER_XT_TARGET_TCPMSS + + config NETFILTER_XT_TARGET_TCPOPTSTRIP + tristate '"TCPOPTSTRIP" target support' +- depends on IP_NF_MANGLE || IP6_NF_MANGLE ++ depends on IP_NF_MANGLE || IP6_NF_MANGLE || NFT_COMPAT + depends on NETFILTER_ADVANCED + help + This option adds a "TCPOPTSTRIP" target, which allows you to strip +-- +2.53.0 + diff --git a/queue-6.6/netfilter-xtables-fix-up-kconfig-dependencies.patch b/queue-6.6/netfilter-xtables-fix-up-kconfig-dependencies.patch new file mode 100644 index 0000000000..69c1f66473 --- /dev/null +++ b/queue-6.6/netfilter-xtables-fix-up-kconfig-dependencies.patch @@ -0,0 +1,58 @@ +From 152783ad008bcb793a21b8fc2186ae17cd702d56 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 Feb 2024 14:55:53 +0100 +Subject: netfilter: xtables: fix up kconfig dependencies + +From: Florian Westphal + +[ Upstream commit 749d4ef0868c5d8a98e07073791b2198178c93b4 ] + +Randy Dunlap reports arptables build failure: +arp_tables.c:(.text+0x20): undefined reference to `xt_find_table' + +... because recent change removed a 'select' on the xtables core. +Add a "depends" clause on arptables to resolve this. + +Kernel test robot reports another build breakage: +iptable_nat.c:(.text+0x8): undefined reference to `ipt_unregister_table_exit' + +... because of a typo, the nat table selected ip6tables. + +Reported-by: kernel test robot +Reported-by: Randy Dunlap +Closes: https://lore.kernel.org/netfilter-devel/d0dfbaef-046a-4c42-9daa-53636664bf6d@infradead.org/ +Fixes: a9525c7f6219 ("netfilter: xtables: allow xtables-nft only builds") +Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds") +Acked-by: Randy Dunlap +Tested-by: Randy Dunlap # build-tested +Signed-off-by: Florian Westphal +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig +index 7835230872818..8f6e950163a79 100644 +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -217,7 +217,7 @@ config IP_NF_NAT + default m if NETFILTER_ADVANCED=n + select NF_NAT + select NETFILTER_XT_NAT +- select IP6_NF_IPTABLES_LEGACY ++ select IP_NF_IPTABLES_LEGACY + help + This enables the `nat' table in iptables. This allows masquerading, + port forwarding and other forms of full Network Address Port +@@ -329,6 +329,7 @@ config NFT_COMPAT_ARP + config IP_NF_ARPFILTER + tristate "arptables-legacy packet filtering support" + select IP_NF_ARPTABLES ++ depends on NETFILTER_XTABLES + help + ARP packet filtering defines a table `filter', which has a series of + rules for simple ARP packet filtering at local input and +-- +2.53.0 + diff --git a/queue-6.6/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch b/queue-6.6/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch new file mode 100644 index 0000000000..ea53610531 --- /dev/null +++ b/queue-6.6/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch @@ -0,0 +1,80 @@ +From d1b5449f69dc8ba1e1a2bb85f6da285d79e1531d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:47 +0100 +Subject: netfs: Fix overrun check in netfs_extract_user_iter() + +From: David Howells + +[ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ] + +Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills +pages[], then those pages don't get included in the iterator constructed at +the end of the function. If there was an overfill, memory corruption has +already happened. + +Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into a BVEC iterator") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/iterator.c | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c +index 781ea403498e3..9a047ca863fe5 100644 +--- a/fs/netfs/iterator.c ++++ b/fs/netfs/iterator.c +@@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + break; + } + +- if (ret > count) { +- pr_err("get_pages rc=%zd more than %zu\n", ret, count); ++ if (WARN(ret > count, ++ "%s: extract_pages overrun %zd > %zu bytes\n", ++ __func__, ret, count)) { ++ ret = -EIO; + break; + } + +- count -= ret; +- ret += offset; +- cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE); +- +- if (npages + cur_npages > max_pages) { +- pr_err("Out of bvec array capacity (%u vs %u)\n", +- npages + cur_npages, max_pages); ++ cur_npages = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ if (WARN(cur_npages > max_pages - npages, ++ "%s: extract_pages overrun %u > %u pages\n", ++ __func__, npages + cur_npages, max_pages)) { ++ ret = -EIO; + break; + } + ++ count -= ret; ++ ret += offset; ++ + for (i = 0; i < cur_npages; i++) { + len = ret > PAGE_SIZE ? PAGE_SIZE : ret; + bvec_set_page(bv + npages + i, *pages++, len - offset, offset); +@@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + npages += cur_npages; + } + ++ /* Note: Don't try to clean up after EIO. Either we got no pages, so ++ * nothing to clean up, or we got a buffer overrun, memory corruption ++ * and can't trust the stuff in the buffer (a WARN was emitted). ++ */ ++ + if (ret < 0 && (ret == -ENOMEM || npages == 0)) { + for (i = 0; i < npages; i++) + unpin_user_page(bv[i].bv_page); +-- +2.53.0 + diff --git a/queue-6.6/pds_core-add-an-error-code-check-in-pdsc_dl_info_get.patch b/queue-6.6/pds_core-add-an-error-code-check-in-pdsc_dl_info_get.patch new file mode 100644 index 0000000000..d895a9b030 --- /dev/null +++ b/queue-6.6/pds_core-add-an-error-code-check-in-pdsc_dl_info_get.patch @@ -0,0 +1,37 @@ +From c626ad6b767e3bff32870257fc74a06ea8668df9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Oct 2023 16:33:52 +0800 +Subject: pds_core: add an error code check in pdsc_dl_info_get + +From: Su Hui + +[ Upstream commit a1e4c334cbc9a80578c3784f8a3e7076bb19578d ] + +check the value of 'ret' after call 'devlink_info_version_stored_put'. + +Signed-off-by: Su Hui +Reviewed-by: Shannon Nelson +Link: https://lore.kernel.org/r/20231019083351.1526484-1-suhui@nfschina.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 3d4432d34c19 ("pds_core: ensure null-termination for firmware version strings") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/devlink.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c +index bee70e46e34c6..dfbd6d39136f4 100644 +--- a/drivers/net/ethernet/amd/pds_core/devlink.c ++++ b/drivers/net/ethernet/amd/pds_core/devlink.c +@@ -126,6 +126,8 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + snprintf(buf, sizeof(buf), "fw.slot_%d", i); + err = devlink_info_version_stored_put(req, buf, + fw_list.fw_names[i].fw_version); ++ if (err) ++ return err; + } + + err = devlink_info_version_running_put(req, +-- +2.53.0 + diff --git a/queue-6.6/pds_core-ensure-null-termination-for-firmware-versio.patch b/queue-6.6/pds_core-ensure-null-termination-for-firmware-versio.patch new file mode 100644 index 0000000000..29a0cbf160 --- /dev/null +++ b/queue-6.6/pds_core-ensure-null-termination-for-firmware-versio.patch @@ -0,0 +1,47 @@ +From f0f5ec3bc83c2d908a3935649b760a5a40200125 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:58:42 +0000 +Subject: pds_core: ensure null-termination for firmware version strings + +From: Nikhil P. Rao + +[ Upstream commit 3d4432d34c1992701289cbe12df9fd024f315998 ] + +The driver passes fw_version directly to devlink_info_version_stored_put() +without ensuring null-termination. While current firmware null-terminates +these strings, the driver should not rely on this behavior. Add explicit +null-termination to prevent potential issues if firmware behavior changes. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c +index dfbd6d39136f4..3144bad75a4df 100644 +--- a/drivers/net/ethernet/amd/pds_core/devlink.c ++++ b/drivers/net/ethernet/amd/pds_core/devlink.c +@@ -120,12 +120,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + + listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); + for (i = 0; i < listlen; i++) { ++ char *fw_ver = fw_list.fw_names[i].fw_version; ++ + if (i < ARRAY_SIZE(fw_slotnames)) + strscpy(buf, fw_slotnames[i], sizeof(buf)); + else + snprintf(buf, sizeof(buf), "fw.slot_%d", i); +- err = devlink_info_version_stored_put(req, buf, +- fw_list.fw_names[i].fw_version); ++ fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0'; ++ err = devlink_info_version_stored_put(req, buf, fw_ver); + if (err) + return err; + } +-- +2.53.0 + diff --git a/queue-6.6/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch b/queue-6.6/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch new file mode 100644 index 0000000000..8380fa5d2a --- /dev/null +++ b/queue-6.6/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch @@ -0,0 +1,50 @@ +From a47e189be2955c8ff1d263a9d6440aeb6223ccfc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:07 +0000 +Subject: pds_core: fix debugfs_lookup dentry leak and error handling + +From: Nikhil P. Rao + +[ Upstream commit dc416e32baaeb620b9809e9e25fc7b30889686e9 ] + +debugfs_lookup() returns a dentry with an elevated reference count that +must be released with dput(). The current code discards the returned +dentry without calling dput(), causing a reference leak on every +firmware reset recovery. + +Additionally, when CONFIG_DEBUG_FS is disabled, debugfs_lookup() +returns ERR_PTR(-ENODEV), not NULL. The current check passes for error +pointers and would call dput() on an invalid pointer, causing a crash. + +Fixes: bc90fbe0c318 ("pds_core: Rework teardown/setup flow to be more common") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/debugfs.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c +index 51f3f73a839a9..33b8e8d1fd4c6 100644 +--- a/drivers/net/ethernet/amd/pds_core/debugfs.c ++++ b/drivers/net/ethernet/amd/pds_core/debugfs.c +@@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity); + + void pdsc_debugfs_add_ident(struct pdsc *pdsc) + { ++ struct dentry *dentry; ++ + /* This file will already exist in the reset flow */ +- if (debugfs_lookup("identity", pdsc->dentry)) ++ dentry = debugfs_lookup("identity", pdsc->dentry); ++ if (!IS_ERR_OR_NULL(dentry)) { ++ dput(dentry); + return; ++ } + + debugfs_create_file("identity", 0400, pdsc->dentry, + pdsc, &identity_fops); +-- +2.53.0 + diff --git a/queue-6.6/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch b/queue-6.6/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch new file mode 100644 index 0000000000..3c208ab71b --- /dev/null +++ b/queue-6.6/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch @@ -0,0 +1,62 @@ +From 8e6ba9a37b7986a11320075f8a8bfd9be1552f6b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:05 +0000 +Subject: pds_core: fix error handling in pdsc_devcmd_wait + +From: Nikhil P. Rao + +[ Upstream commit 0e46b6635b03d29807f810c3b415c4755a3f958d ] + +Fix two cases where pdsc_devcmd_wait() returns stale success from +the completion register instead of an error: + +1. FW crash: If firmware stops running, the wait loop breaks early with + running=false. The condition "if ((!done || timeout) && running)" is + false, so error handling is bypassed and stale status is returned. + Check !running first and return -ENXIO. + +2. Timeout: If a command times out, err is set to -ETIMEDOUT but then + overwritten by pdsc_err_to_errno(status) which reads stale status. + Return -ETIMEDOUT immediately after cleaning up. + +Both errors now propagate to pdsc_devcmd_locked() which queues +health_work for recovery. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/dev.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c +index e65a1632df505..0e9398503a04d 100644 +--- a/drivers/net/ethernet/amd/pds_core/dev.c ++++ b/drivers/net/ethernet/amd/pds_core/dev.c +@@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds) + dev_dbg(dev, "DEVCMD %d %s after %ld secs\n", + opcode, pdsc_devcmd_str(opcode), duration / HZ); + +- if ((!done || timeout) && running) { ++ if (!running) { ++ dev_err(dev, "DEVCMD %d %s fw not running\n", ++ opcode, pdsc_devcmd_str(opcode)); ++ pdsc_devcmd_clean(pdsc); ++ return -ENXIO; ++ } ++ ++ if (!done || timeout) { + dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n", + opcode, pdsc_devcmd_str(opcode), done, timeout, + max_seconds); +- err = -ETIMEDOUT; + pdsc_devcmd_clean(pdsc); ++ return -ETIMEDOUT; + } + + status = pdsc_devcmd_status(pdsc); +-- +2.53.0 + diff --git a/queue-6.6/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-6.6/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..ada751ab5c --- /dev/null +++ b/queue-6.6/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From 19ecd55f2a84d9f4f5d010003b10eca7e9d15217 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 04f4fb4bed702..f882bc57649c7 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-6.6/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch b/queue-6.6/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch new file mode 100644 index 0000000000..6b6953f31f --- /dev/null +++ b/queue-6.6/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch @@ -0,0 +1,56 @@ +From 23d7f0fb1ce538d488aceffc7b795f164a34e6c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:44:58 +0530 +Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for + sm8150 + +From: Maulik Shah + +[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not +support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC +pin and already reflected in the wake irq map. + +Remove the unsupported wakeup interrupts from the map. + +Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") +Reviewed-by: Konrad Dybcio +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c +index f8f5bee74f1dc..565aab84835cb 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c ++++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c +@@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, + { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, +- { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, +- { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, ++ { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, ++ { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, + { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, + { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, +- { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, ++ { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, + { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, + { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, + { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, +- { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, ++ { 144, 115 }, { 147, 102 }, { 150, 107 }, + { 152, 108 }, { 153, 109 } + }; + +-- +2.53.0 + diff --git a/queue-6.6/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-6.6/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..1689ad9c3b --- /dev/null +++ b/queue-6.6/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From a5171911b83d0d629527fd0753aba34a60bccf00 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 6b23ba78e028f..f7bc252650d5a 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-6.6/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-6.6/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..c75d01a819 --- /dev/null +++ b/queue-6.6/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From e18d2354cdd8f77312b537888c2a0537e48b79bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 52535576772ad..aeedc77bed7fc 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-6.6/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-6.6/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..4f9a4aa2f7 --- /dev/null +++ b/queue-6.6/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From 200fb102691778f586a91404820618a3c30a311b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index 79390e9e888a7..346fbad34e24a 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -657,12 +657,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-6.6/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-6.6/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..eb6293dbc4 --- /dev/null +++ b/queue-6.6/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From 74c5d386b375196d90e6aec8fd6efa3f13813587 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index 5d13452bb947a..0e0c4022e61a6 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -272,12 +272,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-6.6/powerpc-time-remove-redundant-preempt_disable-enable.patch b/queue-6.6/powerpc-time-remove-redundant-preempt_disable-enable.patch new file mode 100644 index 0000000000..ec0e57161d --- /dev/null +++ b/queue-6.6/powerpc-time-remove-redundant-preempt_disable-enable.patch @@ -0,0 +1,98 @@ +From 38ea4bc7750fd4605db7667e28f412bc242b5c5e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 13:44:13 +0530 +Subject: powerpc/time: Remove redundant preempt_disable|enable() calls from + arch_irq_work_raise() + +From: Sayali Patil + +[ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] + +A kernel panic is observed when handling machine check exceptions from +real mode. + + BUG: Unable to handle kernel data access on read at 0xc00000006be21300 + Oops: Kernel access of bad area, sig: 11 [#1] + MSR: 8000000000001003 CR: 88222248 XER: 00000005 + CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 + NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 + LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 + Call Trace: + [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 + [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 + +The crash occurs because arch_irq_work_raise() calls preempt_disable() +from machine check exception (MCE) handlers running in real mode. In +this context, accessing the preempt_count can fault, leading to the panic. + +The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix +oops due to perf_event_do_pending call") to avoid races while raising +irq work from exception context. + +Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when +queueing work on the local CPU") added preemption protection in +irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per +cpu atomics instead of regular atomics") added equivalent +protection in irq_work_queue_on() before reaching arch_irq_work_raise(): + + irq_work_queue() / irq_work_queue_on() + -> preempt_disable() + -> __irq_work_queue_local() + -> irq_work_raise() + -> arch_irq_work_raise() + +As a result, callers other than mce_irq_work_raise() already execute +with preemption disabled, making the additional +preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +redundant. + +The arch_irq_work_raise() function executes in NMI context when called +from MCE handler. Hence we will not be preempted or scheduled out since +we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove +the preempt_disable()/preempt_enable() calls from here. + +Remove it to avoid accessing preempt_count from real mode context. + +Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") +Suggested-by: Mahesh Salgaonkar +Acked-by: Shrikanth Hegde +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Sayali Patil +[Maddy: Fixed the commit title] +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/kernel/time.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c +index df20cf201f74d..dda35c404a676 100644 +--- a/arch/powerpc/kernel/time.c ++++ b/arch/powerpc/kernel/time.c +@@ -436,6 +436,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); + + #endif /* 32 vs 64 bit */ + ++/* ++ * Must be called with preemption disabled since it updates ++ * per-CPU irq_work state and programs the local CPU decrementer. ++ */ + void arch_irq_work_raise(void) + { + /* +@@ -449,10 +453,8 @@ void arch_irq_work_raise(void) + * which could get tangled up if we're messing with the same state + * here. + */ +- preempt_disable(); + set_irq_work_pending_flag(); + set_dec(1); +- preempt_enable(); + } + + static void set_dec_or_work(u64 val) +-- +2.53.0 + diff --git a/queue-6.6/ptrace-convert-ptrace_attach-to-use-lock-guards.patch b/queue-6.6/ptrace-convert-ptrace_attach-to-use-lock-guards.patch new file mode 100644 index 0000000000..46a9cc64a2 --- /dev/null +++ b/queue-6.6/ptrace-convert-ptrace_attach-to-use-lock-guards.patch @@ -0,0 +1,259 @@ +From 41023097ad8a4a40cda3469760a0e514252e51b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 Sep 2023 13:24:21 +0200 +Subject: ptrace: Convert ptrace_attach() to use lock guards + +From: Peter Zijlstra + +[ Upstream commit 5431fdd2c181dd2eac218e45b44deb2925fa48f0 ] + +Created as testing for the conditional guard infrastructure. +Specifically this makes use of the following form: + + scoped_cond_guard (mutex_intr, return -ERESTARTNOINTR, + &task->signal->cred_guard_mutex) { + ... + } + ... + return 0; + +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Oleg Nesterov +Link: https://lkml.kernel.org/r/20231102110706.568467727%40infradead.org +Stable-dep-of: 60a1969fae62 ("ALSA: seq: Serialize UMP output teardown with event_input") +Signed-off-by: Sasha Levin +--- + include/linux/sched/task.h | 2 + + include/linux/spinlock.h | 26 ++++++++ + kernel/ptrace.c | 128 ++++++++++++++++++------------------- + 3 files changed, 89 insertions(+), 67 deletions(-) + +diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h +index 8dbecab4c4000..fc19837c8083e 100644 +--- a/include/linux/sched/task.h ++++ b/include/linux/sched/task.h +@@ -227,4 +227,6 @@ static inline void task_unlock(struct task_struct *p) + spin_unlock(&p->alloc_lock); + } + ++DEFINE_GUARD(task_lock, struct task_struct *, task_lock(_T), task_unlock(_T)) ++ + #endif /* _LINUX_SCHED_TASK_H */ +diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h +index ceb56b39c70f7..90bc853cafb6a 100644 +--- a/include/linux/spinlock.h ++++ b/include/linux/spinlock.h +@@ -548,5 +548,31 @@ DEFINE_LOCK_GUARD_1(spinlock_irqsave, spinlock_t, + DEFINE_LOCK_GUARD_1_COND(spinlock_irqsave, _try, + spin_trylock_irqsave(_T->lock, _T->flags)) + ++DEFINE_LOCK_GUARD_1(read_lock, rwlock_t, ++ read_lock(_T->lock), ++ read_unlock(_T->lock)) ++ ++DEFINE_LOCK_GUARD_1(read_lock_irq, rwlock_t, ++ read_lock_irq(_T->lock), ++ read_unlock_irq(_T->lock)) ++ ++DEFINE_LOCK_GUARD_1(read_lock_irqsave, rwlock_t, ++ read_lock_irqsave(_T->lock, _T->flags), ++ read_unlock_irqrestore(_T->lock, _T->flags), ++ unsigned long flags) ++ ++DEFINE_LOCK_GUARD_1(write_lock, rwlock_t, ++ write_lock(_T->lock), ++ write_unlock(_T->lock)) ++ ++DEFINE_LOCK_GUARD_1(write_lock_irq, rwlock_t, ++ write_lock_irq(_T->lock), ++ write_unlock_irq(_T->lock)) ++ ++DEFINE_LOCK_GUARD_1(write_lock_irqsave, rwlock_t, ++ write_lock_irqsave(_T->lock, _T->flags), ++ write_unlock_irqrestore(_T->lock, _T->flags), ++ unsigned long flags) ++ + #undef __LINUX_INSIDE_SPINLOCK_H + #endif /* __LINUX_SPINLOCK_H */ +diff --git a/kernel/ptrace.c b/kernel/ptrace.c +index 3c7d122a37fb4..6196a495d9c0c 100644 +--- a/kernel/ptrace.c ++++ b/kernel/ptrace.c +@@ -396,6 +396,34 @@ static int check_ptrace_options(unsigned long data) + return 0; + } + ++static inline void ptrace_set_stopped(struct task_struct *task) ++{ ++ guard(spinlock)(&task->sighand->siglock); ++ ++ /* ++ * If the task is already STOPPED, set JOBCTL_TRAP_STOP and ++ * TRAPPING, and kick it so that it transits to TRACED. TRAPPING ++ * will be cleared if the child completes the transition or any ++ * event which clears the group stop states happens. We'll wait ++ * for the transition to complete before returning from this ++ * function. ++ * ++ * This hides STOPPED -> RUNNING -> TRACED transition from the ++ * attaching thread but a different thread in the same group can ++ * still observe the transient RUNNING state. IOW, if another ++ * thread's WNOHANG wait(2) on the stopped tracee races against ++ * ATTACH, the wait(2) may fail due to the transient RUNNING. ++ * ++ * The following task_is_stopped() test is safe as both transitions ++ * in and out of STOPPED are protected by siglock. ++ */ ++ if (task_is_stopped(task) && ++ task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING)) { ++ task->jobctl &= ~JOBCTL_STOPPED; ++ signal_wake_up_state(task, __TASK_STOPPED); ++ } ++} ++ + static int ptrace_attach(struct task_struct *task, long request, + unsigned long addr, + unsigned long flags) +@@ -403,17 +431,17 @@ static int ptrace_attach(struct task_struct *task, long request, + bool seize = (request == PTRACE_SEIZE); + int retval; + +- retval = -EIO; + if (seize) { + if (addr != 0) +- goto out; ++ return -EIO; + /* + * This duplicates the check in check_ptrace_options() because + * ptrace_attach() and ptrace_setoptions() have historically + * used different error codes for unknown ptrace options. + */ + if (flags & ~(unsigned long)PTRACE_O_MASK) +- goto out; ++ return -EIO; ++ + retval = check_ptrace_options(flags); + if (retval) + return retval; +@@ -424,88 +452,54 @@ static int ptrace_attach(struct task_struct *task, long request, + + audit_ptrace(task); + +- retval = -EPERM; + if (unlikely(task->flags & PF_KTHREAD)) +- goto out; ++ return -EPERM; + if (same_thread_group(task, current)) +- goto out; ++ return -EPERM; + + /* + * Protect exec's credential calculations against our interference; + * SUID, SGID and LSM creds get determined differently + * under ptrace. + */ +- retval = -ERESTARTNOINTR; +- if (mutex_lock_interruptible(&task->signal->cred_guard_mutex)) +- goto out; ++ scoped_cond_guard (mutex_intr, return -ERESTARTNOINTR, ++ &task->signal->cred_guard_mutex) { + +- task_lock(task); +- retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); +- task_unlock(task); +- if (retval) +- goto unlock_creds; ++ scoped_guard (task_lock, task) { ++ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); ++ if (retval) ++ return retval; ++ } + +- write_lock_irq(&tasklist_lock); +- retval = -EPERM; +- if (unlikely(task->exit_state)) +- goto unlock_tasklist; +- if (task->ptrace) +- goto unlock_tasklist; ++ scoped_guard (write_lock_irq, &tasklist_lock) { ++ if (unlikely(task->exit_state)) ++ return -EPERM; ++ if (task->ptrace) ++ return -EPERM; + +- task->ptrace = flags; ++ task->ptrace = flags; + +- ptrace_link(task, current); ++ ptrace_link(task, current); + +- /* SEIZE doesn't trap tracee on attach */ +- if (!seize) +- send_sig_info(SIGSTOP, SEND_SIG_PRIV, task); ++ /* SEIZE doesn't trap tracee on attach */ ++ if (!seize) ++ send_sig_info(SIGSTOP, SEND_SIG_PRIV, task); + +- spin_lock(&task->sighand->siglock); ++ ptrace_set_stopped(task); ++ } ++ } + + /* +- * If the task is already STOPPED, set JOBCTL_TRAP_STOP and +- * TRAPPING, and kick it so that it transits to TRACED. TRAPPING +- * will be cleared if the child completes the transition or any +- * event which clears the group stop states happens. We'll wait +- * for the transition to complete before returning from this +- * function. +- * +- * This hides STOPPED -> RUNNING -> TRACED transition from the +- * attaching thread but a different thread in the same group can +- * still observe the transient RUNNING state. IOW, if another +- * thread's WNOHANG wait(2) on the stopped tracee races against +- * ATTACH, the wait(2) may fail due to the transient RUNNING. +- * +- * The following task_is_stopped() test is safe as both transitions +- * in and out of STOPPED are protected by siglock. ++ * We do not bother to change retval or clear JOBCTL_TRAPPING ++ * if wait_on_bit() was interrupted by SIGKILL. The tracer will ++ * not return to user-mode, it will exit and clear this bit in ++ * __ptrace_unlink() if it wasn't already cleared by the tracee; ++ * and until then nobody can ptrace this task. + */ +- if (task_is_stopped(task) && +- task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING)) { +- task->jobctl &= ~JOBCTL_STOPPED; +- signal_wake_up_state(task, __TASK_STOPPED); +- } +- +- spin_unlock(&task->sighand->siglock); +- +- retval = 0; +-unlock_tasklist: +- write_unlock_irq(&tasklist_lock); +-unlock_creds: +- mutex_unlock(&task->signal->cred_guard_mutex); +-out: +- if (!retval) { +- /* +- * We do not bother to change retval or clear JOBCTL_TRAPPING +- * if wait_on_bit() was interrupted by SIGKILL. The tracer will +- * not return to user-mode, it will exit and clear this bit in +- * __ptrace_unlink() if it wasn't already cleared by the tracee; +- * and until then nobody can ptrace this task. +- */ +- wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT, TASK_KILLABLE); +- proc_ptrace_connector(task, PTRACE_ATTACH); +- } ++ wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT, TASK_KILLABLE); ++ proc_ptrace_connector(task, PTRACE_ATTACH); + +- return retval; ++ return 0; + } + + /** +-- +2.53.0 + diff --git a/queue-6.6/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-6.6/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..173b2042f2 --- /dev/null +++ b/queue-6.6/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From 7afb89e4e76b43c3e3b4720318bdf478a775eb98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 3f305e694fe8c..1b1c6ea4ee5a4 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 81b6807ebf..583e0653d8 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -94,3 +94,94 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +hid-uclogic-fix-regression-of-input-name-assignment.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch +arm-integrator-fix-early-initialization.patch +alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch +btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-arptables-allow-xtables-nft-only-builds.patch +netfilter-xtables-allow-xtables-nft-only-builds.patch +netfilter-ebtables-allow-xtables-nft-only-builds.patch +netfilter-xtables-fix-up-kconfig-dependencies.patch +netfilter-arptables-select-netfilter_family_arp-when.patch +netfilter-make-legacy-configs-user-selectable.patch +netfilter-exclude-legacy-tables-on-preempt_rt.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-x_tables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch +test_kprobes-clear-kprobes-between-test-runs.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +net-lan966x-avoid-unregistering-netdev-on-register-f.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +irqchip-ath79-cpu-remove-unused-function.patch +irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch +zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch +netfs-fix-overrun-check-in-netfs_extract_user_iter.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch +net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +powerpc-time-remove-redundant-preempt_disable-enable.patch +net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch +net-phy-dp83tc811-add-reading-of-abilities.patch +x86-xen-fix-xen_e820_swap_entry_with_ram.patch +tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch +net-mlx5-do-not-restore-destination-less-tc-rules.patch +spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +net-bridge-flush-multicast-groups-when-snooping-is-d.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch +pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch +ptrace-convert-ptrace_attach-to-use-lock-guards.patch +alsa-seq-ump-use-guard-for-locking.patch +alsa-seq-serialize-ump-output-teardown-with-event_in.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +bluetooth-btmtk-add-the-function-to-get-the-fw-name.patch +bluetooth-btusb-mediatek-refactor-the-function-btusb.patch +bluetooth-btmtk-rename-btmediatek_data.patch +bluetooth-btmtk-move-btusb_mtk_hci_wmt_sync-to-btmtk.patch +bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch +net-ag71xx-check-error-for-platform_get_irq.patch +bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch +string-add-mem_is_zero-helper-to-check-if-memory-are.patch +gpiolib-cdev-use-mem_is_zero-instead-of-memchr_inv-s.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch +ipv6-route-unregister-netdevice-notifier-on-bpf-init.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch +pds_core-add-an-error-code-check-in-pdsc_dl_info_get.patch +pds_core-ensure-null-termination-for-firmware-versio.patch +net-gro-don-t-merge-zcopy-skbs.patch +loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch diff --git a/queue-6.6/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch b/queue-6.6/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch new file mode 100644 index 0000000000..fa8c91c8d3 --- /dev/null +++ b/queue-6.6/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch @@ -0,0 +1,38 @@ +From c2e24dca6b228e8bf3fb6555271e036fa0346bfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 01:55:37 +0800 +Subject: spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() + +From: Felix Gu + +[ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] + +When DMA read times out in mtk_snand_read_page_cache(), the original code +erroneously jumped to cleanup label which skips DMA unmapping and ECC +disable, causing a resource leak. + +Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") +Signed-off-by: Felix Gu +Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-mtk-snfi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c +index 22f3b22d77ad8..f960c2c2f6b81 100644 +--- a/drivers/spi/spi-mtk-snfi.c ++++ b/drivers/spi/spi-mtk-snfi.c +@@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; +- goto cleanup; ++ goto cleanup2; + } + + // Wait for BUS_SEC_CNTR returning expected value +-- +2.53.0 + diff --git a/queue-6.6/string-add-mem_is_zero-helper-to-check-if-memory-are.patch b/queue-6.6/string-add-mem_is_zero-helper-to-check-if-memory-are.patch new file mode 100644 index 0000000000..3e2e23a417 --- /dev/null +++ b/queue-6.6/string-add-mem_is_zero-helper-to-check-if-memory-are.patch @@ -0,0 +1,51 @@ +From 9c6396dee720b9703eccab6e847ba57aed1b1a93 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Aug 2024 13:00:34 +0300 +Subject: string: add mem_is_zero() helper to check if memory area is all zeros + +From: Jani Nikula + +[ Upstream commit 3942bb49728ad9e1f94d953a88af169a8f5d8099 ] + +Almost two thirds of the memchr_inv() usages check if the memory area is +all zeros, with no interest in where in the buffer the first non-zero +byte is located. Checking for !memchr_inv(s, 0, n) is also not very +intuitive or discoverable. Add an explicit mem_is_zero() helper for this +use case. + +Reviewed-by: Kees Cook +Reviewed-by: Andy Shevchenko +Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com +Signed-off-by: Jani Nikula +Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed") +Signed-off-by: Sasha Levin +--- + include/linux/string.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/include/linux/string.h b/include/linux/string.h +index ce137830a0b99..bbcaceb8fb6f5 100644 +--- a/include/linux/string.h ++++ b/include/linux/string.h +@@ -212,6 +212,18 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt) + void *memchr_inv(const void *s, int c, size_t n); + char *strreplace(char *str, char old, char new); + ++/** ++ * mem_is_zero - Check if an area of memory is all 0's. ++ * @s: The memory area ++ * @n: The size of the area ++ * ++ * Return: True if the area of memory is all 0's. ++ */ ++static inline bool mem_is_zero(const void *s, size_t n) ++{ ++ return !memchr_inv(s, 0, n); ++} ++ + extern void kfree_const(const void *x); + + extern char *kstrdup(const char *s, gfp_t gfp) __malloc; +-- +2.53.0 + diff --git a/queue-6.6/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-6.6/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..f176d8f837 --- /dev/null +++ b/queue-6.6/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From 89f12bfcfb3724abce206e1c4e1773f25ad9bbad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index a6f9192b4e53c..c7a1f763e464e 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1115,7 +1115,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-6.6/test_kprobes-clear-kprobes-between-test-runs.patch b/queue-6.6/test_kprobes-clear-kprobes-between-test-runs.patch new file mode 100644 index 0000000000..1c912fe2ac --- /dev/null +++ b/queue-6.6/test_kprobes-clear-kprobes-between-test-runs.patch @@ -0,0 +1,128 @@ +From 3243b1886e5f9af137a089f1bb692f6489e41bc1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: test_kprobes: clear kprobes between test runs + +From: Martin Kaiser + +[ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ] + +Running the kprobes sanity tests twice makes all tests fail and +eventually crashes the kernel. + +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # Totals: pass:5 fail:0 skip:0 total:5 + ok 1 kprobes_test +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 + Expected 0 == register_kprobe(&kp), but + register_kprobe(&kp) == -22 (0xffffffffffffffea) +... + Unable to handle kernel paging request ... + +The testsuite defines several kprobes and kretprobes as static variables +that are preserved across test runs. + +After register_kprobe and unregister_kprobe, a kprobe contains some +leftover data that must be cleared before the kprobe can be registered +again. The tests are setting symbol_name to define the probe location. +Address and flags must be cleared. + +The existing code clears some of the probes between subsequent tests, but +not between two test runs. The leftover data from a previous test run +makes the registrations fail in the next run. + +Move the cleanups for all kprobes into kprobes_test_init, this function +is called before each single test (including the first test of a test +run). + +Link: https://lore.kernel.org/all/20260507134615.1010905-1-martin@kaiser.cx/ + +Fixes: e44e81c5b90f ("kprobes: convert tests to kunit") +Signed-off-by: Martin Kaiser +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + lib/test_kprobes.c | 29 ++++++++++++++++++----------- + 1 file changed, 18 insertions(+), 11 deletions(-) + +diff --git a/lib/test_kprobes.c b/lib/test_kprobes.c +index 0648f7154f5c4..cd7a452ab3032 100644 +--- a/lib/test_kprobes.c ++++ b/lib/test_kprobes.c +@@ -12,6 +12,12 @@ + + #define div_factor 3 + ++#define KP_CLEAR(_kp) \ ++do { \ ++ (_kp).addr = NULL; \ ++ (_kp).flags = 0; \ ++} while (0) ++ + static u32 rand1, preh_val, posth_val; + static u32 (*target)(u32 value); + static u32 (*recursed_target)(u32 value); +@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) + + current_test = test; + +- /* addr and flags should be cleard for reusing kprobe. */ +- kp.addr = NULL; +- kp.flags = 0; +- + KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); + preh_val = 0; + posth_val = 0; +@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) + struct kretprobe *rps[2] = {&rp, &rp2}; + + current_test = test; +- /* addr and flags should be cleard for reusing kprobe. */ +- rp.kp.addr = NULL; +- rp.kp.flags = 0; + KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); + + krph_val = 0; +@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) + unsigned long myretaddr = (unsigned long)__builtin_return_address(0); + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + /* + * Run the stacktrace_driver() to record correct return address in +@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + struct kretprobe *rps[2] = {&rp3, &rp4}; + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); + +@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + + static int kprobes_test_init(struct kunit *test) + { ++ KP_CLEAR(kp); ++ KP_CLEAR(kp2); ++ KP_CLEAR(kp_missed); ++#ifdef CONFIG_KRETPROBES ++ KP_CLEAR(rp.kp); ++ KP_CLEAR(rp2.kp); ++#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE ++ KP_CLEAR(rp3.kp); ++ KP_CLEAR(rp4.kp); ++#endif ++#endif ++ + target = kprobe_target; + target2 = kprobe_target2; + recursed_target = kprobe_recursed_target; +-- +2.53.0 + diff --git a/queue-6.6/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch b/queue-6.6/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch new file mode 100644 index 0000000000..43366020ae --- /dev/null +++ b/queue-6.6/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch @@ -0,0 +1,125 @@ +From 88e4e0efa55e8cec4b3f698e34bb547885a4406c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 08:58:25 -0400 +Subject: tls: Preserve sk_err across recvmsg() when data has been copied + +From: Chuck Lever + +[ Upstream commit f508262ae9f21fe0e6c0749948b9dc7dd5a62a70 ] + +The sk_err check in tls_rx_rec_wait() consumes the error via +sock_error(), which clears sk_err atomically. When the caller +(tls_sw_recvmsg, tls_sw_splice_read, or tls_sw_read_sock) already +has bytes copied to userspace, it returns those bytes and discards +the error from this call. sk_err is now zero on the socket, so the +next read syscall observes only RCV_SHUTDOWN and reports a clean +EOF instead of the actual error (typically -ECONNRESET). + +The race is reachable when tls_read_flush_backlog()'s periodic +sk_flush_backlog() triggers tcp_reset() in the middle of a +multi-record read. + +Pass a has_copied flag to tls_rx_rec_wait(). When has_copied is +false, consume sk_err via sock_error() as before. When has_copied +is true, report the error from READ_ONCE() but leave sk_err set: +the caller returns the byte count and discards the err from this +call, and the next read syscall surfaces the preserved sk_err. This +mirrors the tcp_recvmsg() preserve-and-surface pattern. + +The decrypt-abort path is unaffected: tls_err_abort() raises +sk_err to EBADMSG after tls_rx_rec_wait() returns, and nothing +on the caller's return path consumes it, so the EBADMSG surfaces +on the next read. + +tls_sw_splice_read() passes has_copied=false: it processes +one record per call, so no bytes have been copied within the +function when tls_rx_rec_wait() runs. A reset that arrives +between iterations of splice_direct_to_actor() (the sendfile() +path) is still consumed by sock_error() in the later call, and the +outer loop returns the prior iterations' byte count and drops the +error. tcp_splice_read() exhibits the same pattern at the iteration +boundary; addressing it belongs at the splice_direct_to_actor() +layer and is out of scope here. + +Fixes: c46b01839f7a ("tls: rx: periodically flush socket backlog") +Suggested-by: Jakub Kicinski +Signed-off-by: Chuck Lever +Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 0c2e9724083ee..cdc415de6da2d 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -1366,9 +1366,14 @@ void tls_sw_splice_eof(struct socket *sock) + mutex_unlock(&tls_ctx->tx_lock); + } + ++/* When has_copied is true the caller has already moved bytes to ++ * userspace. Report sk_err but leave it set so the next read ++ * surfaces it instead of a spurious EOF, otherwise sk_err is ++ * consumed via sock_error(). ++ */ + static int + tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, +- bool released) ++ bool released, bool has_copied) + { + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); +@@ -1382,8 +1387,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + if (!sk_psock_queue_empty(psock)) + return 0; + +- if (sk->sk_err) ++ if (sk->sk_err) { ++ if (has_copied) ++ return -READ_ONCE(sk->sk_err); + return sock_error(sk); ++ } + + if (ret < 0) + return ret; +@@ -1419,7 +1427,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + } + + if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) +- return tls_rx_rec_wait(sk, psock, nonblock, false); ++ return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); + + return 1; + } +@@ -2077,7 +2085,7 @@ int tls_sw_recvmsg(struct sock *sk, + int to_decrypt, chunk; + + err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, +- released); ++ released, !!(decrypted + copied)); + if (err <= 0) { + if (psock) { + chunk = sk_msg_recvmsg(sk, psock, msg, len, +@@ -2265,7 +2273,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct tls_decrypt_arg darg; + + err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, +- true); ++ true, false); + if (err <= 0) + goto splice_read_end; + +@@ -2351,7 +2359,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, + } else { + struct tls_decrypt_arg darg; + +- err = tls_rx_rec_wait(sk, NULL, true, released); ++ err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); + if (err <= 0) + goto read_sock_end; + +-- +2.53.0 + diff --git a/queue-6.6/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-6.6/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..1d84ec83cd --- /dev/null +++ b/queue-6.6/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 80dfc80956c6d15d2ed8fc6382c50fa2be0e3a0b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index 597f47397c726..59115079c7982 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1354,10 +1354,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-6.6/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch b/queue-6.6/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch new file mode 100644 index 0000000000..7601792163 --- /dev/null +++ b/queue-6.6/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch @@ -0,0 +1,39 @@ +From aafdeaa81191ce0a1398a8e881b38d67face5f71 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:40 +0200 +Subject: wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm() + +From: Nicolas Escande + +[ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ] + +This is similar to what was fixed by previous patches. We have a call +to ath11k_wmi_cmd_send() which does check the return value, but forgot +to free the related skb on error. + +Fixes: b43310e44edc ("wifi: ath11k: factory test mode support") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/testmode.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c +index 43bb23265d346..b9f0ef458988c 100644 +--- a/drivers/net/wireless/ath/ath11k/testmode.c ++++ b/drivers/net/wireless/ath/ath11k/testmode.c +@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret); ++ dev_kfree_skb(skb); + goto out; + } + +-- +2.53.0 + diff --git a/queue-6.6/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-6.6/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..9c4cfab8fd --- /dev/null +++ b/queue-6.6/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,77 @@ +From 9cc576b56e15381decbc26769ca46cf863ae04ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index f491aeb22a883..a23f20fb1ebd4 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -9090,6 +9090,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9103,14 +9104,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9125,7 +9132,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +-- +2.53.0 + diff --git a/queue-6.6/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch b/queue-6.6/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch new file mode 100644 index 0000000000..b7933f0218 --- /dev/null +++ b/queue-6.6/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch @@ -0,0 +1,73 @@ +From 6f77c9f722ee4c14332a8583fa81865eb03d76ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:50:35 +0100 +Subject: wifi: ath11k: fix peer resolution on rx path when peer_id=0 + +From: Matthew Leach + +[ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] + +It has been observed that on certain chipsets a peer can be assigned +peer_id=0. For reception of non-aggregated MPDUs this is fine as +ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer +based upon the source MAC address. On an aggregated link, the mpdu_start +header is only populated by hardware on the first sub-MSDU. This causes +the peer resolution to be skipped for the subsequent MSDUs and the +encryption type of these frames to be set to an incorrect value, +resulting in these MSDUs being dropped by ieee80211. + +ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 +ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 + +Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and +ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID +of 0 to be resolved. + +Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 + +Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") +Reviewed-by: Baochen Qiang +Signed-off-by: Matthew Leach +Reviewed-by: P Praneesh +Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- + drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index d2d994e094809..31fd92bd9efe8 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2217,8 +2217,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) + + lockdep_assert_held(&ab->base_lock); + +- if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + + if (peer) + return peer; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 2bc95026335e3..8cb212449da77 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1469,11 +1469,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; +- u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); +- if (peer_id) +- ppdu_info->peer_id = peer_id; ++ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +-- +2.53.0 + diff --git a/queue-6.6/x86-xen-fix-xen_e820_swap_entry_with_ram.patch b/queue-6.6/x86-xen-fix-xen_e820_swap_entry_with_ram.patch new file mode 100644 index 0000000000..1f0b0b25df --- /dev/null +++ b/queue-6.6/x86-xen-fix-xen_e820_swap_entry_with_ram.patch @@ -0,0 +1,39 @@ +From d28a66511853b98280795fb1ed1bd13f030c570d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 12:24:17 +0200 +Subject: x86/xen: Fix xen_e820_swap_entry_with_ram() + +From: Juergen Gross + +[ Upstream commit 28e03f78e69cf6628b81f24777799778528a84c1 ] + +When swapping a not page-aligned E820 map entry with RAM, the start +address of the modified entry is calculated wrong (the offset into the +page is subtracted instead of being added to the page address). + +Fixes: be35d91c8880 ("xen: tolerate ACPI NVS memory overlapping with Xen allocated memory") +Reported-by: Jan Beulich +Reviewed-by: Jan Beulich +Signed-off-by: Juergen Gross +Message-ID: <20260505102417.208138-1-jgross@suse.com> +Signed-off-by: Sasha Levin +--- + arch/x86/xen/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c +index ec3ffb94807d3..3d5236fa54dd6 100644 +--- a/arch/x86/xen/setup.c ++++ b/arch/x86/xen/setup.c +@@ -656,7 +656,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) + /* Fill new entry (keep size and page offset). */ + entry->type = swap_entry->type; + entry->addr = entry_end - swap_size + +- swap_addr - swap_entry->addr; ++ swap_entry->addr - swap_addr; + entry->size = swap_entry->size; + + /* Convert old entry to RAM, align to pages. */ +-- +2.53.0 + diff --git a/queue-6.6/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch b/queue-6.6/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch new file mode 100644 index 0000000000..ec75a57766 --- /dev/null +++ b/queue-6.6/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch @@ -0,0 +1,51 @@ +From a58443624964b15129902a579c60856840061d0e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 22:58:15 +0200 +Subject: zonefs: handle integer overflow in zonefs_fname_to_fno + +From: Johannes Thumshirn + +[ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ] + +In zonefs the file name in one of the two directories corresponds to the +zone number. + +Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), +where the parsing of the zone number from the file name can overflow the +'long' data type. + +Add a check for integer overflows and if the fno 'long' did overflow +return -ENOENT. + +Reported-by: Alexey Dobriyan +Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed") +Signed-off-by: Johannes Thumshirn +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + fs/zonefs/super.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c +index cc364669d723d..f1ebe46084570 100644 +--- a/fs/zonefs/super.c ++++ b/fs/zonefs/super.c +@@ -627,10 +627,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname) + return c - '0'; + + for (i = 0, rname = name + len - 1; i < len; i++, rname--) { ++ long digit; ++ + c = *rname; + if (!isdigit(c)) + return -ENOENT; +- fno += (c - '0') * shift; ++ digit = (c - '0') * shift; ++ if (check_add_overflow(fno, digit, &fno)) ++ return -ENOENT; + shift *= 10; + } + +-- +2.53.0 + diff --git a/queue-7.0/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch b/queue-7.0/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch new file mode 100644 index 0000000000..c25ea0678f --- /dev/null +++ b/queue-7.0/accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch @@ -0,0 +1,78 @@ +From 032998a31b2ba43e8b29e3f58d0fa36798fba341 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:39:01 -0700 +Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap + +From: Zack McKevitt + +[ Upstream commit aa16b2bc0f02709919e2435f531406531e5bcc69 ] + +The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to +(re)mapping beyond the VMA if the BO is too large. This can cause use +after free issues when munmap() unmaps only the VMA region and not the +additional mappings. To prevent this, check the remaining size of the +VMA before remapping and truncate the remapped length if sg->length is +too large. + +Reported-by: Lukas Maar +Fixes: ff13be830333 ("accel/qaic: Add datapath") +Reviewed-by: Karol Wachowski +Signed-off-by: Zack McKevitt +Reviewed-by: Jeff Hugo +[jhugo: fix braces from checkpatch --strict] +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_data.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c +index 95300c2f7d8af..1e4c579d27256 100644 +--- a/drivers/accel/qaic/qaic_data.c ++++ b/drivers/accel/qaic/qaic_data.c +@@ -606,8 +606,11 @@ static const struct vm_operations_struct drm_vm_ops = { + static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) + { + struct qaic_bo *bo = to_qaic_bo(obj); ++ unsigned long remap_start; + unsigned long offset = 0; ++ unsigned long remap_end; + struct scatterlist *sg; ++ unsigned long length; + int ret = 0; + + if (drm_gem_is_imported(obj)) +@@ -615,11 +618,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc + + for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { + if (sg_page(sg)) { ++ /* if sg is too large for the VMA, so truncate it to fit */ ++ if (check_add_overflow(vma->vm_start, offset, &remap_start)) ++ return -EINVAL; ++ if (check_add_overflow(remap_start, sg->length, &remap_end)) ++ return -EINVAL; ++ ++ if (remap_end > vma->vm_end) { ++ if (check_sub_overflow(vma->vm_end, remap_start, &length)) ++ return -EINVAL; ++ } else { ++ length = sg->length; ++ } ++ ++ if (length == 0) ++ goto out; ++ + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), +- sg->length, vma->vm_page_prot); ++ length, vma->vm_page_prot); + if (ret) + goto out; +- offset += sg->length; ++ offset += length; + } + } + +-- +2.53.0 + diff --git a/queue-7.0/afs-fix-the-locking-used-by-afs_get_link.patch b/queue-7.0/afs-fix-the-locking-used-by-afs_get_link.patch new file mode 100644 index 0000000000..3455746921 --- /dev/null +++ b/queue-7.0/afs-fix-the-locking-used-by-afs_get_link.patch @@ -0,0 +1,815 @@ +From 4e7c6f769edb491381204291e8c43e7073a9866b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:34:01 +0100 +Subject: afs: Fix the locking used by afs_get_link() + +From: David Howells + +[ Upstream commit c0410adf3da6db46f3513411fcf95e63c2f1d1ad ] + +The afs filesystem in the kernel doesn't do locking correctly for symbolic +links. There are a number of problems: + + (1) It doesn't do any locking around afs_read_single() to prevent races + between multiple ->get_link() calls, thereby allowing the possibility + of leaks. + + (2) It doesn't use RCU barriering when accessing the buffer pointers + during RCU pathwalk. + + (3) It can race with another thread updating the contents of the symlink + if a third party updated it on the server. + +Fix this by the following means: + + (0) Move symlink handling into its own file as this makes it more + complicated. + + (1) Take the validate_lock around afs_read_single() to prevent races + between multiple ->get_link() calls. + + (2) Keep a separate copy of the symlink contents with an rcu_head. This + is always going to be a lot smaller than a page, so it can be + kmalloc'd and save quite a bit of memory. It also needs a refcount + for non-RCU pathwalk. + + (3) Split the symlink read and write-to-cache routines in afs from those + for directories. + + (4) Discard the I/O buffer as soon as the write-to-cache completes as this + is a full page (plus a folio_queue). + + (5) If there's no cache, discard the I/O buffer immediately after reading + and copying if there is no cache. + +Fixes: eae9e78951bb ("afs: Use netfslib for symlinks, allowing them to be cached") +Fixes: 6698c02d64b2 ("afs: Locally initialise the contents of a new symlink on creation") +Closes: https://sashiko.dev/#/patchset/20260326104544.509518-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-25-dhowells@redhat.com +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/afs/Makefile | 1 + + fs/afs/dir.c | 68 +++++------ + fs/afs/fsclient.c | 4 +- + fs/afs/inode.c | 96 +-------------- + fs/afs/internal.h | 34 ++++-- + fs/afs/symlink.c | 278 ++++++++++++++++++++++++++++++++++++++++++++ + fs/afs/validation.c | 14 ++- + fs/afs/yfsclient.c | 4 +- + 8 files changed, 357 insertions(+), 142 deletions(-) + create mode 100644 fs/afs/symlink.c + +diff --git a/fs/afs/Makefile b/fs/afs/Makefile +index b49b8fe682f39..0d8f1982d596c 100644 +--- a/fs/afs/Makefile ++++ b/fs/afs/Makefile +@@ -30,6 +30,7 @@ kafs-y := \ + server.o \ + server_list.o \ + super.o \ ++ symlink.o \ + validation.o \ + vlclient.o \ + vl_alias.o \ +diff --git a/fs/afs/dir.c b/fs/afs/dir.c +index 068a892d39c4e..99ad0058694c9 100644 +--- a/fs/afs/dir.c ++++ b/fs/afs/dir.c +@@ -44,6 +44,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir, + static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags); ++static int afs_dir_writepages(struct address_space *mapping, ++ struct writeback_control *wbc); + + const struct file_operations afs_dir_file_operations = { + .open = afs_dir_open, +@@ -68,7 +70,7 @@ const struct inode_operations afs_dir_inode_operations = { + }; + + const struct address_space_operations afs_dir_aops = { +- .writepages = afs_single_writepages, ++ .writepages = afs_dir_writepages, + }; + + const struct dentry_operations afs_fs_dentry_operations = { +@@ -233,22 +235,13 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file) + struct iov_iter iter; + ssize_t ret; + loff_t i_size; +- bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) && +- !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags)); + + i_size = i_size_read(&dvnode->netfs.inode); +- if (is_dir) { +- if (i_size < AFS_DIR_BLOCK_SIZE) +- return afs_bad(dvnode, afs_file_error_dir_small); +- if (i_size > AFS_DIR_BLOCK_SIZE * 1024) { +- trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); +- return -EFBIG; +- } +- } else { +- if (i_size > AFSPATHMAX) { +- trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); +- return -EFBIG; +- } ++ if (i_size < AFS_DIR_BLOCK_SIZE) ++ return afs_bad(dvnode, afs_file_error_dir_small); ++ if (i_size > AFS_DIR_BLOCK_SIZE * 1024) { ++ trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big); ++ return -EFBIG; + } + + /* Expand the storage. TODO: Shrink the storage too. */ +@@ -277,24 +270,18 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file) + * buffer. + */ + ret = -ESTALE; +- } else if (is_dir) { ++ } else { + int ret2 = afs_dir_check(dvnode); + + if (ret2 < 0) + ret = ret2; +- } else if (i_size < folioq_folio_size(dvnode->directory, 0)) { +- /* NUL-terminate a symlink. */ +- char *symlink = kmap_local_folio(folioq_folio(dvnode->directory, 0), 0); +- +- symlink[i_size] = 0; +- kunmap_local(symlink); + } + } + + return ret; + } + +-ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file) ++static ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file) + { + ssize_t ret; + +@@ -1763,13 +1750,20 @@ static int afs_link(struct dentry *from, struct inode *dir, + return ret; + } + ++static void afs_symlink_put(struct afs_operation *op) ++{ ++ kfree(op->create.symlink); ++ op->create.symlink = NULL; ++ afs_create_put(op); ++} ++ + static const struct afs_operation_ops afs_symlink_operation = { + .issue_afs_rpc = afs_fs_symlink, + .issue_yfs_rpc = yfs_fs_symlink, + .success = afs_create_success, + .aborted = afs_check_for_remote_deletion, + .edit_dir = afs_create_edit_dir, +- .put = afs_create_put, ++ .put = afs_symlink_put, + }; + + /* +@@ -1779,7 +1773,9 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *content) + { + struct afs_operation *op; ++ struct afs_symlink *symlink; + struct afs_vnode *dvnode = AFS_FS_I(dir); ++ size_t clen = strlen(content); + int ret; + + _enter("{%llx:%llu},{%pd},%s", +@@ -1791,12 +1787,20 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir, + goto error; + + ret = -EINVAL; +- if (strlen(content) >= AFSPATHMAX) ++ if (clen >= AFSPATHMAX) ++ goto error; ++ ++ ret = -ENOMEM; ++ symlink = kmalloc_flex(struct afs_symlink, content, clen + 1, GFP_KERNEL); ++ if (!symlink) + goto error; ++ refcount_set(&symlink->ref, 1); ++ memcpy(symlink->content, content, clen + 1); + + op = afs_alloc_operation(NULL, dvnode->volume); + if (IS_ERR(op)) { + ret = PTR_ERR(op); ++ kfree(symlink); + goto error; + } + +@@ -1808,7 +1812,7 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir, + op->dentry = dentry; + op->ops = &afs_symlink_operation; + op->create.reason = afs_edit_dir_for_symlink; +- op->create.symlink = content; ++ op->create.symlink = symlink; + op->mtime = current_time(dir); + ret = afs_do_sync_operation(op); + afs_dir_unuse_cookie(dvnode, ret); +@@ -2192,15 +2196,13 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir, + } + + /* +- * Write the file contents to the cache as a single blob. ++ * Write the directory contents to the cache as a single blob. + */ +-int afs_single_writepages(struct address_space *mapping, +- struct writeback_control *wbc) ++static int afs_dir_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) + { + struct afs_vnode *dvnode = AFS_FS_I(mapping->host); + struct iov_iter iter; +- bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) && +- !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags)); + int ret = 0; + + /* Need to lock to prevent the folio queue and folios from being thrown +@@ -2215,9 +2217,7 @@ int afs_single_writepages(struct address_space *mapping, + down_read(&dvnode->validate_lock); + } + +- if (is_dir ? +- test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : +- atomic64_read(&dvnode->cb_expires_at) != AFS_NO_CB_PROMISE) { ++ if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { + iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, + i_size_read(&dvnode->netfs.inode)); + ret = netfs_writeback_single(mapping, wbc, &iter); +diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c +index 95494d5f2b8a9..a2ffd60889f89 100644 +--- a/fs/afs/fsclient.c ++++ b/fs/afs/fsclient.c +@@ -886,7 +886,7 @@ void afs_fs_symlink(struct afs_operation *op) + namesz = name->len; + padsz = (4 - (namesz & 3)) & 3; + +- c_namesz = strlen(op->create.symlink); ++ c_namesz = strlen(op->create.symlink->content); + c_padsz = (4 - (c_namesz & 3)) & 3; + + reqsz = (6 * 4) + namesz + padsz + c_namesz + c_padsz + (6 * 4); +@@ -910,7 +910,7 @@ void afs_fs_symlink(struct afs_operation *op) + bp = (void *) bp + padsz; + } + *bp++ = htonl(c_namesz); +- memcpy(bp, op->create.symlink, c_namesz); ++ memcpy(bp, op->create.symlink->content, c_namesz); + bp = (void *) bp + c_namesz; + if (c_padsz > 0) { + memset(bp, 0, c_padsz); +diff --git a/fs/afs/inode.c b/fs/afs/inode.c +index df95b39ed308e..de72256f00db5 100644 +--- a/fs/afs/inode.c ++++ b/fs/afs/inode.c +@@ -25,96 +25,6 @@ + #include "internal.h" + #include "afs_fs.h" + +-void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op) +-{ +- size_t size = strlen(op->create.symlink) + 1; +- size_t dsize = 0; +- char *p; +- +- if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size, +- mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0) +- return; +- +- vnode->directory_size = dsize; +- p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0); +- memcpy(p, op->create.symlink, size); +- kunmap_local(p); +- set_bit(AFS_VNODE_DIR_READ, &vnode->flags); +- netfs_single_mark_inode_dirty(&vnode->netfs.inode); +-} +- +-static void afs_put_link(void *arg) +-{ +- struct folio *folio = virt_to_folio(arg); +- +- kunmap_local(arg); +- folio_put(folio); +-} +- +-const char *afs_get_link(struct dentry *dentry, struct inode *inode, +- struct delayed_call *callback) +-{ +- struct afs_vnode *vnode = AFS_FS_I(inode); +- struct folio *folio; +- char *content; +- ssize_t ret; +- +- if (!dentry) { +- /* RCU pathwalk. */ +- if (!test_bit(AFS_VNODE_DIR_READ, &vnode->flags) || !afs_check_validity(vnode)) +- return ERR_PTR(-ECHILD); +- goto good; +- } +- +- if (test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) +- goto fetch; +- +- ret = afs_validate(vnode, NULL); +- if (ret < 0) +- return ERR_PTR(ret); +- +- if (!test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && +- test_bit(AFS_VNODE_DIR_READ, &vnode->flags)) +- goto good; +- +-fetch: +- ret = afs_read_single(vnode, NULL); +- if (ret < 0) +- return ERR_PTR(ret); +- set_bit(AFS_VNODE_DIR_READ, &vnode->flags); +- +-good: +- folio = folioq_folio(vnode->directory, 0); +- folio_get(folio); +- content = kmap_local_folio(folio, 0); +- set_delayed_call(callback, afs_put_link, content); +- return content; +-} +- +-int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +-{ +- DEFINE_DELAYED_CALL(done); +- const char *content; +- int len; +- +- content = afs_get_link(dentry, d_inode(dentry), &done); +- if (IS_ERR(content)) { +- do_delayed_call(&done); +- return PTR_ERR(content); +- } +- +- len = umin(strlen(content), buflen); +- if (copy_to_user(buffer, content, len)) +- len = -EFAULT; +- do_delayed_call(&done); +- return len; +-} +- +-static const struct inode_operations afs_symlink_inode_operations = { +- .get_link = afs_get_link, +- .readlink = afs_readlink, +-}; +- + static noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode) + { + static unsigned long once_only; +@@ -214,7 +124,7 @@ static int afs_inode_init_from_status(struct afs_operation *op, + inode->i_mode = S_IFLNK | status->mode; + inode->i_op = &afs_symlink_inode_operations; + } +- inode->i_mapping->a_ops = &afs_dir_aops; ++ inode->i_mapping->a_ops = &afs_symlink_aops; + inode_nohighmem(inode); + mapping_set_release_always(inode->i_mapping); + break; +@@ -769,12 +679,14 @@ void afs_evict_inode(struct inode *inode) + .range_end = LLONG_MAX, + }; + +- afs_single_writepages(inode->i_mapping, &wbc); ++ inode->i_mapping->a_ops->writepages(inode->i_mapping, &wbc); + } + + netfs_wait_for_outstanding_io(inode); + truncate_inode_pages_final(&inode->i_data); + netfs_free_folioq_buffer(vnode->directory); ++ if (vnode->symlink) ++ afs_evict_symlink(vnode); + + afs_set_cache_aux(vnode, &aux); + netfs_clear_inode_writeback(inode, &aux); +diff --git a/fs/afs/internal.h b/fs/afs/internal.h +index fb0449d024ff2..dc89c3c602032 100644 +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -711,6 +711,7 @@ struct afs_vnode { + #define AFS_VNODE_DIR_READ 11 /* Set if we've read a dir's contents */ + + struct folio_queue *directory; /* Directory contents */ ++ struct afs_symlink __rcu *symlink; /* Symlink content */ + struct list_head wb_keys; /* List of keys available for writeback */ + struct list_head pending_locks; /* locks waiting to be granted */ + struct list_head granted_locks; /* locks granted on this file */ +@@ -777,6 +778,15 @@ struct afs_permits { + struct afs_permit permits[] __counted_by(nr_permits); /* List of permits sorted by key pointer */ + }; + ++/* ++ * Copy of symlink content for normal use. ++ */ ++struct afs_symlink { ++ struct rcu_head rcu; ++ refcount_t ref; ++ char content[]; ++}; ++ + /* + * Error prioritisation and accumulation. + */ +@@ -888,7 +898,7 @@ struct afs_operation { + struct { + int reason; /* enum afs_edit_dir_reason */ + mode_t mode; +- const char *symlink; ++ struct afs_symlink *symlink; + } create; + struct { + bool need_rehash; +@@ -1099,13 +1109,10 @@ extern const struct inode_operations afs_dir_inode_operations; + extern const struct address_space_operations afs_dir_aops; + extern const struct dentry_operations afs_fs_dentry_operations; + +-ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file); + ssize_t afs_read_dir(struct afs_vnode *dvnode, struct file *file) + __acquires(&dvnode->validate_lock); + extern void afs_d_release(struct dentry *); + extern void afs_check_for_remote_deletion(struct afs_operation *); +-int afs_single_writepages(struct address_space *mapping, +- struct writeback_control *wbc); + + /* + * dir_edit.c +@@ -1248,10 +1255,6 @@ extern void afs_fs_probe_cleanup(struct afs_net *); + */ + extern const struct afs_operation_ops afs_fetch_status_operation; + +-void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op); +-const char *afs_get_link(struct dentry *dentry, struct inode *inode, +- struct delayed_call *callback); +-int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen); + extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *); + extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *); + extern int afs_ilookup5_test_by_fid(struct inode *, void *); +@@ -1601,6 +1604,21 @@ void afs_detach_volume_from_servers(struct afs_volume *volume, struct afs_server + extern int __init afs_fs_init(void); + extern void afs_fs_exit(void); + ++/* ++ * symlink.c ++ */ ++extern const struct inode_operations afs_symlink_inode_operations; ++extern const struct address_space_operations afs_symlink_aops; ++ ++void afs_invalidate_symlink(struct afs_vnode *vnode); ++void afs_evict_symlink(struct afs_vnode *vnode); ++void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op); ++const char *afs_get_link(struct dentry *dentry, struct inode *inode, ++ struct delayed_call *callback); ++int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen); ++int afs_symlink_writepages(struct address_space *mapping, ++ struct writeback_control *wbc); ++ + /* + * validation.c + */ +diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c +new file mode 100644 +index 0000000000000..ed5868369f372 +--- /dev/null ++++ b/fs/afs/symlink.c +@@ -0,0 +1,278 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* AFS filesystem symbolic link handling ++ * ++ * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved. ++ * Written by David Howells (dhowells@redhat.com) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "internal.h" ++ ++static void afs_put_symlink(struct afs_symlink *symlink) ++{ ++ if (refcount_dec_and_test(&symlink->ref)) ++ kfree_rcu(symlink, rcu); ++} ++ ++static void afs_replace_symlink(struct afs_vnode *vnode, struct afs_symlink *symlink) ++{ ++ struct afs_symlink *old; ++ ++ old = rcu_replace_pointer(vnode->symlink, symlink, ++ lockdep_is_held(&vnode->validate_lock)); ++ if (old) ++ afs_put_symlink(old); ++} ++ ++/* ++ * In the event that a third-party update of a symlink occurs, dispose of the ++ * copy of the old contents. Called under ->validate_lock. ++ */ ++void afs_invalidate_symlink(struct afs_vnode *vnode) ++{ ++ afs_replace_symlink(vnode, NULL); ++} ++ ++/* ++ * Dispose of a symlink copy during inode deletion. ++ */ ++void afs_evict_symlink(struct afs_vnode *vnode) ++{ ++ struct afs_symlink *old; ++ ++ old = rcu_replace_pointer(vnode->symlink, NULL, true); ++ if (old) ++ afs_put_symlink(old); ++ ++} ++ ++/* ++ * Set up a locally created symlink inode for immediate write to the cache. ++ */ ++void afs_init_new_symlink(struct afs_vnode *vnode, struct afs_operation *op) ++{ ++ struct afs_symlink *symlink = op->create.symlink; ++ size_t dsize = 0; ++ size_t size = strlen(symlink->content) + 1; ++ char *p; ++ ++ rcu_assign_pointer(vnode->symlink, symlink); ++ op->create.symlink = NULL; ++ ++ if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) ++ return; ++ ++ if (netfs_alloc_folioq_buffer(NULL, &vnode->directory, &dsize, size, ++ mapping_gfp_mask(vnode->netfs.inode.i_mapping)) < 0) ++ return; ++ ++ vnode->directory_size = dsize; ++ p = kmap_local_folio(folioq_folio(vnode->directory, 0), 0); ++ memcpy(p, symlink->content, size); ++ kunmap_local(p); ++ netfs_single_mark_inode_dirty(&vnode->netfs.inode); ++} ++ ++/* ++ * Read a symlink in a single download. ++ */ ++static ssize_t afs_do_read_symlink(struct afs_vnode *vnode) ++{ ++ struct afs_symlink *symlink; ++ struct iov_iter iter; ++ ssize_t ret; ++ loff_t i_size; ++ ++ i_size = i_size_read(&vnode->netfs.inode); ++ if (i_size > PAGE_SIZE - 1) { ++ trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big); ++ return -EFBIG; ++ } ++ ++ if (!vnode->directory) { ++ size_t cur_size = 0; ++ ++ ret = netfs_alloc_folioq_buffer(NULL, ++ &vnode->directory, &cur_size, PAGE_SIZE, ++ mapping_gfp_mask(vnode->netfs.inode.i_mapping)); ++ vnode->directory_size = PAGE_SIZE - 1; ++ if (ret < 0) ++ return ret; ++ } ++ ++ iov_iter_folio_queue(&iter, ITER_DEST, vnode->directory, 0, 0, PAGE_SIZE); ++ ++ /* AFS requires us to perform the read of a symlink as a single unit to ++ * avoid issues with the content being changed between reads. ++ */ ++ ret = netfs_read_single(&vnode->netfs.inode, NULL, &iter); ++ if (ret >= 0) { ++ i_size = ret; ++ if (i_size > PAGE_SIZE - 1) { ++ trace_afs_file_error(vnode, -EFBIG, afs_file_error_dir_big); ++ return -EFBIG; ++ } ++ vnode->directory_size = i_size; ++ ++ /* Copy the symlink. */ ++ symlink = kmalloc_flex(struct afs_symlink, content, i_size + 1, ++ GFP_KERNEL); ++ if (!symlink) ++ return -ENOMEM; ++ ++ refcount_set(&symlink->ref, 1); ++ symlink->content[i_size] = 0; ++ ++ const char *s = kmap_local_folio(folioq_folio(vnode->directory, 0), 0); ++ ++ memcpy(symlink->content, s, i_size); ++ kunmap_local(s); ++ ++ afs_replace_symlink(vnode, symlink); ++ } ++ ++ if (!fscache_cookie_enabled(netfs_i_cookie(&vnode->netfs))) { ++ netfs_free_folioq_buffer(vnode->directory); ++ vnode->directory = NULL; ++ vnode->directory_size = 0; ++ } ++ ++ return ret; ++} ++ ++static ssize_t afs_read_symlink(struct afs_vnode *vnode) ++{ ++ ssize_t ret; ++ ++ fscache_use_cookie(afs_vnode_cache(vnode), false); ++ ret = afs_do_read_symlink(vnode); ++ fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL); ++ return ret; ++} ++ ++static void afs_put_link(void *arg) ++{ ++ afs_put_symlink(arg); ++} ++ ++const char *afs_get_link(struct dentry *dentry, struct inode *inode, ++ struct delayed_call *callback) ++{ ++ struct afs_symlink *symlink; ++ struct afs_vnode *vnode = AFS_FS_I(inode); ++ ssize_t ret; ++ ++ if (!dentry) { ++ /* RCU pathwalk. */ ++ symlink = rcu_dereference(vnode->symlink); ++ if (!symlink || !afs_check_validity(vnode)) ++ return ERR_PTR(-ECHILD); ++ set_delayed_call(callback, NULL, NULL); ++ return symlink->content; ++ } ++ ++ if (vnode->symlink) { ++ ret = afs_validate(vnode, NULL); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ down_read(&vnode->validate_lock); ++ if (vnode->symlink) ++ goto good; ++ up_read(&vnode->validate_lock); ++ } ++ ++ if (down_write_killable(&vnode->validate_lock) < 0) ++ return ERR_PTR(-ERESTARTSYS); ++ if (!vnode->symlink) { ++ ret = afs_read_symlink(vnode); ++ if (ret < 0) { ++ up_write(&vnode->validate_lock); ++ return ERR_PTR(ret); ++ } ++ } ++ ++ downgrade_write(&vnode->validate_lock); ++ ++good: ++ symlink = rcu_dereference_protected(vnode->symlink, ++ lockdep_is_held(&vnode->validate_lock)); ++ refcount_inc(&symlink->ref); ++ up_read(&vnode->validate_lock); ++ ++ set_delayed_call(callback, afs_put_link, symlink); ++ return symlink->content; ++} ++ ++int afs_readlink(struct dentry *dentry, char __user *buffer, int buflen) ++{ ++ DEFINE_DELAYED_CALL(done); ++ const char *content; ++ int len; ++ ++ content = afs_get_link(dentry, d_inode(dentry), &done); ++ if (IS_ERR(content)) { ++ do_delayed_call(&done); ++ return PTR_ERR(content); ++ } ++ ++ len = umin(strlen(content), buflen); ++ if (copy_to_user(buffer, content, len)) ++ len = -EFAULT; ++ do_delayed_call(&done); ++ return len; ++} ++ ++/* ++ * Write the symlink contents to the cache as a single blob. We then throw ++ * away the page we used to receive it. ++ */ ++int afs_symlink_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) ++{ ++ struct afs_vnode *vnode = AFS_FS_I(mapping->host); ++ struct iov_iter iter; ++ int ret = 0; ++ ++ if (!down_read_trylock(&vnode->validate_lock)) { ++ if (wbc->sync_mode == WB_SYNC_NONE) { ++ /* The VFS will have undirtied the inode. */ ++ netfs_single_mark_inode_dirty(&vnode->netfs.inode); ++ return 0; ++ } ++ down_read(&vnode->validate_lock); ++ } ++ ++ if (vnode->directory && ++ atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE) { ++ iov_iter_folio_queue(&iter, ITER_SOURCE, vnode->directory, 0, 0, ++ i_size_read(&vnode->netfs.inode)); ++ ret = netfs_writeback_single(mapping, wbc, &iter); ++ } ++ ++ if (ret == 0) { ++ mutex_lock(&vnode->netfs.wb_lock); ++ netfs_free_folioq_buffer(vnode->directory); ++ vnode->directory = NULL; ++ vnode->directory_size = 0; ++ mutex_unlock(&vnode->netfs.wb_lock); ++ } else if (ret == 1) { ++ ret = 0; /* Skipped write due to lock conflict. */ ++ } ++ ++ up_read(&vnode->validate_lock); ++ return ret; ++} ++ ++const struct inode_operations afs_symlink_inode_operations = { ++ .get_link = afs_get_link, ++ .readlink = afs_readlink, ++}; ++ ++const struct address_space_operations afs_symlink_aops = { ++ .writepages = afs_symlink_writepages, ++}; +diff --git a/fs/afs/validation.c b/fs/afs/validation.c +index 0ba8336c90250..e997563af658b 100644 +--- a/fs/afs/validation.c ++++ b/fs/afs/validation.c +@@ -465,11 +465,17 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) + vnode->cb_ro_snapshot = cb_ro_snapshot; + vnode->cb_scrub = cb_scrub; + +- /* if the vnode's data version number changed then its contents are +- * different */ ++ /* If the vnode's data version number changed then its contents are ++ * different. Note that afs_apply_status() doesn't set ZAP_DATA on ++ * directories. ++ */ + zap |= test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); +- if (zap) +- afs_zap_data(vnode); ++ if (zap) { ++ if (S_ISREG(vnode->netfs.inode.i_mode)) ++ afs_zap_data(vnode); ++ else if (S_ISLNK(vnode->netfs.inode.i_mode)) ++ afs_invalidate_symlink(vnode); ++ } + up_write(&vnode->validate_lock); + _leave(" = 0"); + return 0; +diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c +index 24fb562ebd33a..d941179730a98 100644 +--- a/fs/afs/yfsclient.c ++++ b/fs/afs/yfsclient.c +@@ -960,7 +960,7 @@ void yfs_fs_symlink(struct afs_operation *op) + + _enter(""); + +- contents_sz = strlen(op->create.symlink); ++ contents_sz = strlen(op->create.symlink->content); + call = afs_alloc_flat_call(op->net, &yfs_RXYFSSymlink, + sizeof(__be32) + + sizeof(struct yfs_xdr_RPCFlags) + +@@ -981,7 +981,7 @@ void yfs_fs_symlink(struct afs_operation *op) + bp = xdr_encode_u32(bp, 0); /* RPC flags */ + bp = xdr_encode_YFSFid(bp, &dvp->fid); + bp = xdr_encode_name(bp, name); +- bp = xdr_encode_string(bp, op->create.symlink, contents_sz); ++ bp = xdr_encode_string(bp, op->create.symlink->content, contents_sz); + bp = xdr_encode_YFSStoreStatus(bp, &mode, &op->mtime); + yfs_check_req(call, bp); + +-- +2.53.0 + diff --git a/queue-7.0/alsa-hda-ca0132-disable-auto-detect-on-manual-output.patch b/queue-7.0/alsa-hda-ca0132-disable-auto-detect-on-manual-output.patch new file mode 100644 index 0000000000..457f211abe --- /dev/null +++ b/queue-7.0/alsa-hda-ca0132-disable-auto-detect-on-manual-output.patch @@ -0,0 +1,118 @@ +From 197f8de6d7bc191be00db28089f0fe206608e52b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 09:58:41 -0500 +Subject: ALSA: hda/ca0132: Disable auto-detect on manual output select + +From: Matt DeVillier + +[ Upstream commit 6fd9f6e870ea285f05102e8e00e6a7f4495a9a02 ] + +Commit 778031e1658d ("ALSA: hda/ca0132: Set HP/Speaker +auto-detect default from headphone pin verb") enables HP/Speaker +auto-detect by default when the headphone pin supports presence detect. + +With auto-detect enabled, ca0132_select_out() and ca0132_alt_select_out() +choose the output from jack presence instead of the manual HP/Speaker +selection. This means selecting speaker output while headphones are +plugged in updates the control state, but audio still routes to the +headphones. + +Treat an explicit manual output selection as a request to leave +auto-detect mode. Clear the HP/Speaker auto-detect switch before applying +the manual selection, and notify userspace so the auto-detect control +state is updated in mixers. Do this for both the normal HP/Speaker +Playback Switch and the alternate Output Select control used by desktop +cards. + +This keeps auto-detect enabled by default for devices with jack presence +detection, while preserving the expected behavior that a manual output +choice takes effect immediately. + +Fixes: 778031e1658d ("ALSA: hda/ca0132: Set HP/Speaker auto-detect default from headphone pin verb") +Signed-off-by: Matt DeVillier +Link: https://lore.kernel.org/CAFTm+6AfeXKf=b2frG4xC5yC4jjM9TkD6c8+dOWWFw6BDjDESw@mail.gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/ca0132.c | 44 +++++++++++++++++++++++++++------------ + 1 file changed, 31 insertions(+), 13 deletions(-) + +diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c +index a0677d7da8e2d..b4e10957ac6db 100644 +--- a/sound/hda/codecs/ca0132.c ++++ b/sound/hda/codecs/ca0132.c +@@ -5508,6 +5508,30 @@ static int zxr_headphone_gain_set(struct hda_codec *codec, long val) + return 0; + } + ++/* ++ * Manual output selection (HP/Speaker Playback Switch or alt Output Select) ++ * is meaningful only when HP/Speaker auto-detect is disabled, since the ++ * select_out path always prefers jack presence when auto-detect is on. When ++ * the user explicitly chooses an output, turn auto-detect off so the manual ++ * choice actually takes effect, and notify userspace so the auto-detect ++ * control reflects the new state. ++ */ ++static void ca0132_disable_hp_auto_detect(struct hda_codec *codec) ++{ ++ struct ca0132_spec *spec = codec->spec; ++ struct snd_kcontrol *kctl; ++ ++ if (!spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]) ++ return; ++ ++ spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID] = 0; ++ kctl = snd_hda_find_mixer_ctl(codec, ++ "HP/Speaker Auto Detect Playback Switch"); ++ if (kctl) ++ snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &kctl->id); ++} ++ + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -5520,14 +5544,11 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, + int auto_jack; + + if (nid == VNID_HP_SEL) { +- auto_jack = +- spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; +- if (!auto_jack) { +- if (ca0132_use_alt_functions(spec)) +- ca0132_alt_select_out(codec); +- else +- ca0132_select_out(codec); +- } ++ ca0132_disable_hp_auto_detect(codec); ++ if (ca0132_use_alt_functions(spec)) ++ ca0132_alt_select_out(codec); ++ else ++ ca0132_select_out(codec); + return 1; + } + +@@ -5988,7 +6009,6 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = NUM_OF_OUTPUTS; +- unsigned int auto_jack; + + if (sel >= items) + return 0; +@@ -5998,10 +6018,8 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, + + spec->out_enum_val = sel; + +- auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; +- +- if (!auto_jack) +- ca0132_alt_select_out(codec); ++ ca0132_disable_hp_auto_detect(codec); ++ ca0132_alt_select_out(codec); + + return 1; + } +-- +2.53.0 + diff --git a/queue-7.0/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch b/queue-7.0/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch new file mode 100644 index 0000000000..f5b9523795 --- /dev/null +++ b/queue-7.0/alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch @@ -0,0 +1,48 @@ +From 82f244632c96c9c03e08755c7f90a57b397c8b97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:12:38 +0800 +Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node + +From: Shuhao Fu + +[ Upstream commit fca7401fe37f7abc6e54147ea560f37279231137 ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers must balance it with acpi_dev_put(). + +cs35l41_hda_read_acpi() stores the returned ACPI device in +cs35l41->dacpi. That reference is normally released by the later +probe cleanup or the remove path, but the NULL-check on +physdev exits before either of those paths can run. + +Drop the lookup reference before returning -ENODEV. + +Fixes: c34b04cc6178 ("ALSA: hda: cs35l41: Fix NULL pointer dereference in cs35l41_hda_read_acpi()") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/side-codecs/cs35l41_hda.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c +index b64890006bb70..acfccc848f82d 100644 +--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c ++++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c +@@ -1896,8 +1896,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i + + cs35l41->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); +- if (!physdev) ++ if (!physdev) { ++ acpi_dev_put(adev); + return -ENODEV; ++ } + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) +-- +2.53.0 + diff --git a/queue-7.0/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch b/queue-7.0/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch new file mode 100644 index 0000000000..7e3499ff34 --- /dev/null +++ b/queue-7.0/alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch @@ -0,0 +1,44 @@ +From aeb2522bbf37d518e065030d4d218be4be2c0c2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:01:39 +0800 +Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion + +From: Shuhao Fu + +[ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ] + +acpi_dev_get_first_match_dev() returns a refcounted ACPI device and +callers are expected to balance it with acpi_dev_put(). + +When no companion is already attached, cs35l56_hda_read_acpi() looks +up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves +the lookup reference held. + +ACPI_COMPANION_SET() does not take ownership of that reference, so +drop it with acpi_dev_put() after attaching the companion. + +Fixes: 73cfbfa9caea ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier") +Signed-off-by: Shuhao Fu +Tested-by: Simon Trimmer +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16 +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/side-codecs/cs35l56_hda.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c +index 4c8d01799931c..cdbc576569efe 100644 +--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c ++++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c +@@ -1041,6 +1041,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) + return -ENODEV; + } + ACPI_COMPANION_SET(cs35l56->base.dev, adev); ++ acpi_dev_put(adev); + } + + /* Initialize things that could be overwritten by a fixup */ +-- +2.53.0 + diff --git a/queue-7.0/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch b/queue-7.0/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch new file mode 100644 index 0000000000..af63f6e14f --- /dev/null +++ b/queue-7.0/alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch @@ -0,0 +1,57 @@ +From d6e69744f9326500b6173feadd56d348a756f87f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 19:15:31 +0800 +Subject: ALSA: hda/realtek: Use ALC287_FIXUP_TXNW2781_I2C for ASUS Strix Gxx5 + +From: Eric Naim + +[ Upstream commit 4372286ac774536e8e68bc6dfa0f0b0152b31fce ] + +These devices were incorrectly using the ALC287_FIXUP_TAS2781_I2C quirk +leading to errors: + +[ 18.765990] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768153] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768476] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found +[ 18.768899] Serial bus multi instantiate pseudo device driver TXNW2781:00: Instantiated 3 I2C devices. + +Use the ALC287_FIXUP_TXNW2781_I2C quirk instead to fix this and restore +speaker audio on affected devices. + +Fixes: 1e9c708dc3ae ("ALSA: hda/tas2781: Add new quirk for Lenovo, ASUS, Dell projects") +Link: https://lore.kernel.org/59fd4aa4-76b9-4984-8db9-a60e55ec6e80@losource.net/ +Closes: https://lore.kernel.org/CACB9z7kjs8rhLstEc8fV29BCTb5dd881JwGozoKdO5cwCb=YwQ@mail.gmail.com +Signed-off-by: Eric Naim +Link: https://patch.msgid.link/20260516111532.111463-1-dnaim@cachyos.org +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/hda/codecs/realtek/alc269.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index 8f7d8337b4bc6..c59021c15d66c 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -7389,12 +7389,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), +-- +2.53.0 + diff --git a/queue-7.0/alsa-scarlett2-add-missing-error-check-when-initiali.patch b/queue-7.0/alsa-scarlett2-add-missing-error-check-when-initiali.patch new file mode 100644 index 0000000000..bcf7337b7e --- /dev/null +++ b/queue-7.0/alsa-scarlett2-add-missing-error-check-when-initiali.patch @@ -0,0 +1,41 @@ +From ffd1dc0f9c7e2c7eb3dcfe6a04cd16e88a44b914 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 10:39:14 +0700 +Subject: ALSA: scarlett2: Add missing error check when initialise Autogain + Status + +From: Robertus Diawan Chris + +[ Upstream commit c0e4fffc0f474b7ed10adee4ab2bc1a66d36fc72 ] + +When initialise new control with scarlett2_add_new_ctl() function for +Autogain Status, scarlett2_add_new_ctl() might throw an error. So, add +error check after initialise new control for Autogain Status. + +This is reported by Coverity Scan with CID 1598781 as UNUSED_VALUE. + +Fixes: 0a995e38dc44 ("ALSA: scarlett2: Add support for software-controllable input gain") +Signed-off-by: Robertus Diawan Chris +Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/mixer_scarlett2.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c +index 4ca96fbee3bc0..8e80a7165faf2 100644 +--- a/sound/usb/mixer_scarlett2.c ++++ b/sound/usb/mixer_scarlett2.c +@@ -6707,6 +6707,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_status_ctl, + i, 1, s, &private->autogain_status_ctls[i]); ++ if (err < 0) ++ return err; + } + + /* Add autogain target controls */ +-- +2.53.0 + diff --git a/queue-7.0/alsa-seq-serialize-ump-output-teardown-with-event_in.patch b/queue-7.0/alsa-seq-serialize-ump-output-teardown-with-event_in.patch new file mode 100644 index 0000000000..ba058a024e --- /dev/null +++ b/queue-7.0/alsa-seq-serialize-ump-output-teardown-with-event_in.patch @@ -0,0 +1,174 @@ +From 97bdccaf940dddd29bb7baf8d47124e766315b8a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:32:49 +0800 +Subject: ALSA: seq: Serialize UMP output teardown with event_input + +From: Zhang Cen + +[ Upstream commit 60a1969fae6209644698fca91c185d153674f631 ] + +seq_ump_process_event() borrows client->out_rfile.output without +synchronizing with the first-open and last-close transition in +seq_ump_client_open() and seq_ump_client_close(). + +The last output unuse can therefore drop opened[STR_OUT] to zero and +release the rawmidi file while an in-flight event_input callback is still +inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream +runtime exposed to teardown before the write path has taken its own +buffer reference. + +Add a per-client rwlock for the event_input-visible output file. Publish +a newly opened output file under the write side, and hold the read side +from the output lookup through snd_rawmidi_kernel_write(). The last +output close copies and clears the visible output file under the write +side, then drops the lock and releases the saved rawmidi file. Use +IRQ-safe rwlock guards because event_input can also be reached from +atomic sequencer delivery. + +The buggy scenario involves two paths, with each column showing the +order within that path: + +path A label: event_input path path B label: last unuse path +1. seq_ump_process_event() reads 1. seq_ump_client_close() + client->out_rfile.output. drops opened[STR_OUT] to zero. +2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release() + has not yet pinned runtime. closes the output file. +3. The writer continues using 3. close_substream() frees + the borrowed substream. substream->runtime. + +This keeps the output substream and runtime alive for the full +event_input write while keeping rawmidi release outside the rwlock. + +KASAN reproduced this as a slab-use-after-free in +snd_rawmidi_kernel_write1(), with allocation through +seq_ump_use()/snd_seq_port_connect() and free through +seq_ump_unuse()/snd_seq_port_disconnect(). + +Suggested-by: Takashi Iwai + +Validation reproduced this kernel report: +KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400 +RIP: 0033:0x7f5528af837f +Read of size 8 +Call trace: + dump_stack_lvl+0x73/0xb0 (?:?) + print_report+0xd1/0x650 (?:?) + srso_alias_return_thunk+0x5/0xfbef5 (?:?) + __virt_addr_valid+0x1a7/0x340 (?:?) + kasan_complete_mode_report_info+0x64/0x200 (?:?) + kasan_report+0xf7/0x130 (?:?) + snd_rawmidi_kernel_write1+0x9d/0x400 (?:?) + __asan_load8+0x82/0xb0 (?:?) + update_stack_state+0x1ef/0x2d0 (?:?) + snd_rawmidi_kernel_write+0x1a/0x20 (?:?) + seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82) + __snd_seq_deliver_single_event+0x8a/0xe0 (?:?) + snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?) + lock_acquire+0x14e/0x2e0 (?:?) + find_held_lock+0x31/0x90 (?:?) + snd_seq_port_use_ptr+0xa6/0xe0 (?:?) + __kasan_check_write+0x18/0x20 (?:?) + do_raw_read_unlock+0x32/0xa0 (?:?) + _raw_read_unlock+0x26/0x50 (?:?) + snd_seq_deliver_single_event+0x45c/0x4b0 (?:?) + snd_seq_deliver_event+0x10d/0x1b0 (?:?) + snd_seq_client_enqueue_event+0x192/0x240 (?:?) + snd_seq_write+0x2cd/0x450 (?:?) + apparmor_file_permission+0x20/0x30 (?:?) + security_file_permission+0x51/0x60 (?:?) + vfs_write+0x1ce/0x850 (?:?) + __fget_files+0x12b/0x220 (?:?) + lock_release+0xc8/0x2a0 (?:?) + __rcu_read_unlock+0x74/0x2d0 (?:?) + __fget_files+0x135/0x220 (?:?) + ksys_write+0x15a/0x180 (?:?) + rcu_is_watching+0x24/0x60 (?:?) + __x64_sys_write+0x46/0x60 (?:?) + x64_sys_call+0x7d/0x20d0 (?:?) + do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) + entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) + +Fixes: 81fd444aa371 ("ALSA: seq: Bind UMP device") +Signed-off-by: Zhang Cen +Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/core/seq/seq_ump_client.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c +index 9079ccfdc8666..ccd93599b493b 100644 +--- a/sound/core/seq/seq_ump_client.c ++++ b/sound/core/seq/seq_ump_client.c +@@ -37,6 +37,7 @@ struct seq_ump_client { + struct snd_ump_endpoint *ump; /* assigned endpoint */ + int seq_client; /* sequencer client id */ + int opened[2]; /* current opens for each direction */ ++ rwlock_t output_lock; /* protects out_rfile output access */ + struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + struct seq_ump_input_buffer input; /* input parser context */ + void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ +@@ -88,6 +89,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + unsigned char type; + int len; + ++ guard(read_lock_irqsave)(&client->output_lock); + substream = client->out_rfile.output; + if (!substream) + return -ENODEV; +@@ -106,6 +108,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, + static int seq_ump_client_open(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + int err; + + guard(mutex)(&ump->open_mutex); +@@ -113,9 +116,11 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + err = snd_rawmidi_kernel_open(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, +- &client->out_rfile); ++ &rfile); + if (err < 0) + return err; ++ scoped_guard(write_lock_irqsave, &client->output_lock) ++ client->out_rfile = rfile; + } + client->opened[dir]++; + return 0; +@@ -125,11 +130,19 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) + static int seq_ump_client_close(struct seq_ump_client *client, int dir) + { + struct snd_ump_endpoint *ump = client->ump; ++ struct snd_rawmidi_file rfile = {}; + + guard(mutex)(&ump->open_mutex); +- if (!--client->opened[dir]) +- if (dir == STR_OUT) +- snd_rawmidi_kernel_release(&client->out_rfile); ++ if (!--client->opened[dir]) { ++ if (dir == STR_OUT) { ++ scoped_guard(write_lock_irqsave, &client->output_lock) { ++ rfile = client->out_rfile; ++ client->out_rfile = (struct snd_rawmidi_file){}; ++ } ++ if (rfile.rmidi) ++ snd_rawmidi_kernel_release(&rfile); ++ } ++ } + return 0; + } + +@@ -467,6 +480,7 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) + + INIT_WORK(&client->group_notify_work, handle_group_notify); + client->ump = ump; ++ rwlock_init(&client->output_lock); + + client->seq_client = + snd_seq_create_kernel_client(card, ump->core.device, +-- +2.53.0 + diff --git a/queue-7.0/arm-dts-renesas-genmai-drop-superfluous-cells.patch b/queue-7.0/arm-dts-renesas-genmai-drop-superfluous-cells.patch new file mode 100644 index 0000000000..fc92ac9147 --- /dev/null +++ b/queue-7.0/arm-dts-renesas-genmai-drop-superfluous-cells.patch @@ -0,0 +1,40 @@ +From f03d290c0e8d4f23415fadf7eadd96d39f5d9229 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:10 +0100 +Subject: ARM: dts: renesas: genmai: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit 714e1d6bba0e0abe5c87c8e189a35fa690540df4 ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-genmai.dts:28.17-55.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +index 3c37565097145..da552a66615e0 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +@@ -34,9 +34,6 @@ flash@18000000 { + clocks = <&mstp9_clks R7S72100_CLK_SPIBSC0>; + power-domains = <&cpg_clocks>; + +- #address-cells = <1>; +- #size-cells = <1>; +- + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; +-- +2.53.0 + diff --git a/queue-7.0/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch b/queue-7.0/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch new file mode 100644 index 0000000000..bb94fe7629 --- /dev/null +++ b/queue-7.0/arm-dts-renesas-rskrza1-drop-superfluous-cells.patch @@ -0,0 +1,39 @@ +From 5065a69944411d60d9b399a571172f6f9fd5f3a1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 00:42:11 +0100 +Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells + +From: Marek Vasut + +[ Upstream commit ab83176d3cf1cf1c1f6e604432905bda4515d17f ] + +Drop superfluous address-cells and size-cells to fix DTC W=1 warning: + + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts:32.17-72.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property + +Signed-off-by: Marek Vasut +Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +index 91178fb9e7210..3306bc9b7bc37 100644 +--- a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts ++++ b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +@@ -36,8 +36,6 @@ flash@18000000 { + power-domains = <&cpg_clocks>; + bank-width = <4>; + device-width = <1>; +- #address-cells = <1>; +- #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; +-- +2.53.0 + diff --git a/queue-7.0/arm-integrator-fix-early-initialization.patch b/queue-7.0/arm-integrator-fix-early-initialization.patch new file mode 100644 index 0000000000..5ff971dfd8 --- /dev/null +++ b/queue-7.0/arm-integrator-fix-early-initialization.patch @@ -0,0 +1,96 @@ +From 120d16aeda2770e36c17d681cb03995c40eb6bd8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 21:15:37 +0200 +Subject: ARM: integrator: Fix early initialization + +From: Guenter Roeck + +[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] + +Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using +syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible +which in turn calls of_syscon_register. This function allocates memory. +Since the memory management code has not been initialized at that time, +the call always fails. It either returns -ENOMEM or crashes as follows. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read +[0000000c] *pgd=00000000 +Internal error: Oops: 5 [#1] ARM +Modules linked in: +CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT +Hardware name: ARM Integrator/CP (Device Tree) +PC is at __kmalloc_cache_noprof+0xec/0x39c +LR is at __kmalloc_cache_noprof+0x34/0x39c +... +Call trace: + __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 + of_syscon_register from device_node_get_regmap+0xa4/0xb0 + device_node_get_regmap from intcp_init_early+0xc/0x40 + intcp_init_early from start_kernel+0x60/0x688 + start_kernel from 0x0 + +The crash is seen due to a dereferenced pointer which is not supposed to be +NULL but is NULL if the memory management subsystem has not been +initialized. The crash is not seen with all versions of gcc. Some versions +such as gcc 9.x apparently do not dereference the pointer, presumably if +tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, +and 13.x. Either case, if the crash is not seen, the call to +syscon_regmap_lookup_by_compatible returns -ENOMEM, and +sched_clock_register is never called. + +Fix the problem by moving the early initialization code into the standard +machine initialization code. + +Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") +Cc: Linus Walleij +Signed-off-by: Guenter Roeck +Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org +Signed-off-by: Arnd Bergmann +Signed-off-by: Sasha Levin +--- + arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c +index 2ed4ded56b3fe..03dfb5f720b7b 100644 +--- a/arch/arm/mach-versatile/integrator_cp.c ++++ b/arch/arm/mach-versatile/integrator_cp.c +@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) + return val; + } + +-static void __init intcp_init_early(void) +-{ +- cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); +- if (IS_ERR(cm_map)) +- return; +- sched_clock_register(intcp_read_sched_clock, 32, 24000000); +-} +- + static void __init intcp_init_irq_of(void) + { + cm_init(); +@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) + { + struct device_node *cpcon; + ++ cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); ++ if (!IS_ERR(cm_map)) ++ sched_clock_register(intcp_read_sched_clock, 32, 24000000); ++ + cpcon = of_find_matching_node(NULL, intcp_syscon_match); + if (!cpcon) + return; +@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { + DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") + .reserve = integrator_reserve, + .map_io = intcp_map_io, +- .init_early = intcp_init_early, + .init_irq = intcp_init_irq_of, + .init_machine = intcp_init_of, + .dt_compat = intcp_dt_board_compat, +-- +2.53.0 + diff --git a/queue-7.0/arm64-dts-renesas-r8a78000-fix-scif-brg_int-clocks.patch b/queue-7.0/arm64-dts-renesas-r8a78000-fix-scif-brg_int-clocks.patch new file mode 100644 index 0000000000..d5559b7f8a --- /dev/null +++ b/queue-7.0/arm64-dts-renesas-r8a78000-fix-scif-brg_int-clocks.patch @@ -0,0 +1,66 @@ +From 1b222f2f0fec3dd7845af254136dac8b93210b30 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 Jan 2026 18:09:51 +0100 +Subject: arm64: dts: renesas: r8a78000: Fix SCIF brg_int clocks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Geert Uytterhoeven + +[ Upstream commit 86637727c11a105499e9faa38f3422dfcf4d211d ] + +According to the documentation, the internal clock input for the BRG is +SGASYNCD4_PERW_BUSφ. + +Fixes: c13a643e2c491f5b ("arm64: dts: renesas: Add R8A78000 SoC support") +Signed-off-by: Geert Uytterhoeven +Link: https://patch.msgid.link/459d360a8332f92b3766b30814e7e1c76169aaf7.1767719254.git.geert+renesas@glider.be +Signed-off-by: Sasha Levin +--- + arch/arm64/boot/dts/renesas/r8a78000.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm64/boot/dts/renesas/r8a78000.dtsi b/arch/arm64/boot/dts/renesas/r8a78000.dtsi +index 3e1c98903cea0..3ec1b53d27828 100644 +--- a/arch/arm64/boot/dts/renesas/r8a78000.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a78000.dtsi +@@ -699,7 +699,7 @@ scif0: serial@c0700000 { + "renesas,rcar-gen5-scif", "renesas,scif"; + reg = <0 0xc0700000 0 0x40>; + interrupts = ; +- clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; ++ clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + status = "disabled"; + }; +@@ -709,7 +709,7 @@ scif1: serial@c0704000 { + "renesas,rcar-gen5-scif", "renesas,scif"; + reg = <0 0xc0704000 0 0x40>; + interrupts = ; +- clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; ++ clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + status = "disabled"; + }; +@@ -719,7 +719,7 @@ scif3: serial@c0708000 { + "renesas,rcar-gen5-scif", "renesas,scif"; + reg = <0 0xc0708000 0 0x40>; + interrupts = ; +- clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; ++ clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + status = "disabled"; + }; +@@ -729,7 +729,7 @@ scif4: serial@c070c000 { + "renesas,rcar-gen5-scif", "renesas,scif"; + reg = <0 0xc070c000 0 0x40>; + interrupts = ; +- clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; ++ clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + status = "disabled"; + }; +-- +2.53.0 + diff --git a/queue-7.0/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch b/queue-7.0/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch new file mode 100644 index 0000000000..fcc17d36ef --- /dev/null +++ b/queue-7.0/asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch @@ -0,0 +1,46 @@ +From f8f4cf0f70d2633df06c25402599d4f9fbfbd360 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 13:42:02 -0300 +Subject: ASoC: amd: acp-sdw-legacy: check CPU DAI name before logging +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Cássio Gabriel + +[ Upstream commit 1afd8f06dcb1d561af3b239c5b14a88b87c13454 ] + +devm_kasprintf() can fail and return NULL. The legacy AMD SoundWire +machine driver logs cpus->dai_name before checking the allocation result. + +Move the debug print after the NULL check, matching the ordering used by +the SOF AMD SoundWire path after commit 5726b68473f7 ("ASoC: amd/sdw_utils: +avoid NULL deref when devm_kasprintf() fails"). + +Fixes: 2981d9b0789c ("ASoC: amd: acp: add soundwire machine driver for legacy stack") +Signed-off-by: Cássio Gabriel +Link: https://patch.msgid.link/20260511-asoc-amd-acp-sdw-legacy-dai-name-null-v1-1-dc6151b6da8a@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +index a9c8d9545281e..ae9579c8511eb 100644 +--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +@@ -260,9 +260,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SDW%d Pin%d", + link_num, cpu_pin_id); +- dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); + if (!cpus->dai_name) + return -ENOMEM; ++ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); + + codec_maps[j].cpu = 0; + codec_maps[j].codec = j; +-- +2.53.0 + diff --git a/queue-7.0/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch b/queue-7.0/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch new file mode 100644 index 0000000000..813f13587e --- /dev/null +++ b/queue-7.0/asoc-codecs-fs210x-fix-possible-buffer-overflow.patch @@ -0,0 +1,44 @@ +From d6860a37083f954a5f09927daea4ed83c57d49a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 21:08:52 +0200 +Subject: ASoC: codecs: fs210x: fix possible buffer overflow + +From: Alexander A. Klimov + +[ Upstream commit 0d435a7ebcd4e97e47673c1ab6fb27f973a053ec ] + +In fs210x_effect_scene_info(), a string was copied like this: + + strscpy(DST, SRC, strlen(SRC) + 1); + +A buffer overflow would happen if strlen(SRC) >= sizeof(DST). +Actually, strscpy() must be used this way: + + strscpy(DST, SRC, sizeof(DST)); + strscpy(DST, SRC); // defaults to sizeof(DST) + +Fixes: 756117701779 ("ASoC: codecs: Add FourSemi FS2104/5S audio amplifier driver") +Signed-off-by: Alexander A. Klimov +Link: https://patch.msgid.link/20260513190852.196723-2-grandmaster@al2klimov.de +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/fs210x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c +index e6195b71adadc..eda716f817b58 100644 +--- a/sound/soc/codecs/fs210x.c ++++ b/sound/soc/codecs/fs210x.c +@@ -968,7 +968,7 @@ static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol, + if (scene->name) + name = scene->name; + +- strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1); ++ strscpy(uinfo->value.enumerated.name, name); + + return 0; + } +-- +2.53.0 + diff --git a/queue-7.0/asoc-cs-amp-lib-fix-missing-dput-after-debugfs_looku.patch b/queue-7.0/asoc-cs-amp-lib-fix-missing-dput-after-debugfs_looku.patch new file mode 100644 index 0000000000..e47712336a --- /dev/null +++ b/queue-7.0/asoc-cs-amp-lib-fix-missing-dput-after-debugfs_looku.patch @@ -0,0 +1,54 @@ +From 598df9b6dba53cbce187211cbf1bde6094ba52c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:25:10 +0100 +Subject: ASoC: cs-amp-lib: Fix missing dput() after debugfs_lookup() + +From: Richard Fitzgerald + +[ Upstream commit ba28a07a9a0b53a538c809e04e517e1ce1f1bee3 ] + +Rewrite cs_amp_create_debugfs() so that dput() will be called on +a valid dentry returned from debugfs_lookup(). + +The pointer returned from debugfs_lookup() must be released by dput(). +The pointer returned from debugfs_create_dir() does not need to be +passed to dput(). + +Signed-off-by: Richard Fitzgerald +Fixes: cdd27fa3298a ("ASoC: cs-amp-lib: Add helpers for factory calibration") +Link: https://patch.msgid.link/20260521122511.987322-3-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs-amp-lib.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c +index 75baeaf64afd0..fae006aa78982 100644 +--- a/sound/soc/codecs/cs-amp-lib.c ++++ b/sound/soc/codecs/cs-amp-lib.c +@@ -831,11 +831,18 @@ EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP + */ + struct dentry *cs_amp_create_debugfs(struct device *dev) + { +- struct dentry *dir; ++ struct dentry *dir, *created; + ++ /* debugfs_lookup() can return NULL or ERR_PTR on error */ + dir = debugfs_lookup("cirrus_logic", NULL); +- if (!dir) +- dir = debugfs_create_dir("cirrus_logic", NULL); ++ if (!IS_ERR_OR_NULL(dir)) { ++ created = debugfs_create_dir(dev_name(dev), dir); ++ dput(dir); ++ ++ return created; ++ } ++ ++ dir = debugfs_create_dir("cirrus_logic", NULL); + + return debugfs_create_dir(dev_name(dev), dir); + } +-- +2.53.0 + diff --git a/queue-7.0/asoc-cs-amp-lib-fix-wrong-sizeof-in-_cs_amp_set_efi_.patch b/queue-7.0/asoc-cs-amp-lib-fix-wrong-sizeof-in-_cs_amp_set_efi_.patch new file mode 100644 index 0000000000..cbe5e10a31 --- /dev/null +++ b/queue-7.0/asoc-cs-amp-lib-fix-wrong-sizeof-in-_cs_amp_set_efi_.patch @@ -0,0 +1,49 @@ +From 09b3ab004efd1b9426cdd8dad6d42ecff21102d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:25:09 +0100 +Subject: ASoC: cs-amp-lib: Fix wrong sizeof() in + _cs_amp_set_efi_calibration_data() + +From: Richard Fitzgerald + +[ Upstream commit 67a52d3ebb5a0ae0c0e23ffa99470d9463179c9f ] + +When calculating data->count replace the incorrect sizeof(data) with use +of struct_offset(). + +The faulty sizeof(data) was incorrectly calculating the size of the +pointer instead of the size of the struct pointed to. As it happens, both +values are 8 on a 64-bit CPU. In the unlikely event of using this code on +a 32-bit CPU the number of available bytes would be calculated 4 larger +than is actually available. + +Instead of changing to sizeof(*data) it has been replaced by +struct_offset() because it has better chance of detecting these sorts of +typos. Also the offset of the data[] array is actually what we want to know +here anyway. + +Signed-off-by: Richard Fitzgerald +Fixes: 2b62e66626f0 ("ASoC: cs-amp-lib: Add function to write calibration to UEFI") +Link: https://patch.msgid.link/20260521122511.987322-2-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs-amp-lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c +index 8b131975143d1..75baeaf64afd0 100644 +--- a/sound/soc/codecs/cs-amp-lib.c ++++ b/sound/soc/codecs/cs-amp-lib.c +@@ -500,7 +500,7 @@ static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, i + * must be set. + */ + if (data->count == 0) +- data->count = (data->size - sizeof(data)) / sizeof(data->data[0]); ++ data->count = (data->size - struct_offset(data, data)) / sizeof(data->data[0]); + + if (amp_index < 0) { + /* Is there already a slot for this target? */ +-- +2.53.0 + diff --git a/queue-7.0/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch b/queue-7.0/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch new file mode 100644 index 0000000000..f2321d6570 --- /dev/null +++ b/queue-7.0/asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch @@ -0,0 +1,48 @@ +From 67c10bac25f140a99b2646fc0b85455bde6c5486 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:30:57 +0100 +Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove() + +From: Richard Fitzgerald + +[ Upstream commit 18e7bd9f2446664053f8c34b72abd4606d22d858 ] + +Use flush_work() instead of cancel_work_sync() to terminate pending IRQ +work in cs35l56_sdw_remove(). And flush_work() again after masking the +interrupts to flush any queueing that was racing with the masking. This is +the same sequence as cs35l56_sdw_system_suspend(). + +cs35l56_sdw_interrupt() takes the pm_runtime to prevent the bus powering- +down before the interrupt status can be read and handled. The work releases +this pm_runtime. So cancelling it, instead of flushing, could leave an +unbalanced pm_runtime. + +Signed-off-by: Richard Fitzgerald +Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") +Link: https://patch.msgid.link/20260521123057.988732-1-rf@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/cs35l56-sdw.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +index 30b3192d6ce9b..8d7772894f10a 100644 +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -560,10 +560,11 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral) + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; +- cancel_work_sync(&cs35l56->sdw_irq_work); ++ flush_work(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ flush_work(&cs35l56->sdw_irq_work); + + cs35l56_remove(cs35l56); + } +-- +2.53.0 + diff --git a/queue-7.0/asoc-intel-sof_sdw-prepare-for-configuration-without.patch b/queue-7.0/asoc-intel-sof_sdw-prepare-for-configuration-without.patch new file mode 100644 index 0000000000..f7c3398a71 --- /dev/null +++ b/queue-7.0/asoc-intel-sof_sdw-prepare-for-configuration-without.patch @@ -0,0 +1,54 @@ +From ff09a6224cdbf18e020f52d2d0316070321e640a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Apr 2026 09:23:35 +0100 +Subject: ASoC: intel: sof_sdw: Prepare for configuration without a jack + +From: Maciej Strozek + +[ Upstream commit d733fb463834cf97a0c667681e236fea0e833a05 ] + +In certain setups of cs42l43 UAJ function may be removed from ACPI and +physically unconnected. Prepare a driver for that configuration by +setting a system clock in the speaker path too. + +Signed-off-by: Maciej Strozek +Link: https://patch.msgid.link/20260403082335.40798-1-mstrozek@opensource.cirrus.com +Signed-off-by: Mark Brown +Stable-dep-of: 5a30862dec5a ("ASoC: sdw_utils: Check speaker component string allocation") +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_cs42l43.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c b/sound/soc/sdw_utils/soc_sdw_cs42l43.c +index 2685ff4f09320..4a451b9d4f137 100644 +--- a/sound/soc/sdw_utils/soc_sdw_cs42l43.c ++++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c +@@ -107,6 +107,7 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS"); + + int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) + { ++ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); +@@ -131,8 +132,15 @@ int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_so + + ret = snd_soc_dapm_add_routes(dapm, cs42l43_spk_map, + ARRAY_SIZE(cs42l43_spk_map)); +- if (ret) ++ if (ret) { + dev_err(card->dev, "cs42l43 speaker map addition failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = snd_soc_component_set_sysclk(component, CS42L43_SYSCLK, CS42L43_SYSCLK_SDW, ++ 0, SND_SOC_CLOCK_IN); ++ if (ret) ++ dev_err(card->dev, "Failed to set sysclk: %d\n", ret); + + return ret; + } +-- +2.53.0 + diff --git a/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch b/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch new file mode 100644 index 0000000000..821aa19537 --- /dev/null +++ b/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch @@ -0,0 +1,41 @@ +From 4bc99beea2d7ee6adfedfa924c780529183edb55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 17:32:23 +0800 +Subject: ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC + +From: Mac Chiang + +[ Upstream commit 9c37daee7c17fa17e8d41089ee1f658b06cb672a ] + +Some devices do not use CODEC_MIC but use the host PCH_DMIC +instead. Add a quirk to skip the CODEC_MIC DAI when it is not present +in disco table, ensuring the correct capture device is used. + +If CODEC_MIC is present, it continues to be used as default. + +Fixes: 9489db97f6f0 ("ASoC: sdw_utils: add SmartMic DAI for RT712 VB") +Signed-off-by: Mac Chiang +Signed-off-by: Bard Liao +Link: https://patch.msgid.link/20260508093224.1246282-2-yung-chuan.liao@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index 0e67d9f34cba3..827243d09f008 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -189,6 +189,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, ++ .quirk = SOC_SDW_CODEC_MIC, ++ .quirk_exclude = true, + }, + }, + .dai_num = 3, +-- +2.53.0 + diff --git a/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch b/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch new file mode 100644 index 0000000000..264982e94d --- /dev/null +++ b/queue-7.0/asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch @@ -0,0 +1,39 @@ +From 4917f62a474d9a4ead6e8bdb3617fdfa581775e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 17:32:24 +0800 +Subject: ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC + +From: Mac Chiang + +[ Upstream commit fa749a77bdc50f0d695aaf81f1bd55967d77d10f ] + +Add a quirk to skip the CODEC_MIC DAI when it is not present. +This ensures PCH_DMIC is used as the fallback; otherwise, +CODEC_MIC remains the default. + +Fixes: 846a8d3cf3ba ("ASoC: Intel: soc-acpi-intel-ptl-match: Add rt721 support") +Signed-off-by: Mac Chiang +Signed-off-by: Bard Liao +Link: https://patch.msgid.link/20260508093224.1246282-3-yung-chuan.liao@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index 827243d09f008..f54043e5ff450 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -463,6 +463,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + .rtd_init = asoc_sdw_rt_dmic_rtd_init, ++ .quirk = SOC_SDW_CODEC_MIC, ++ .quirk_exclude = true, + }, + }, + .dai_num = 3, +-- +2.53.0 + diff --git a/queue-7.0/asoc-sdw_utils-check-speaker-component-string-alloca.patch b/queue-7.0/asoc-sdw_utils-check-speaker-component-string-alloca.patch new file mode 100644 index 0000000000..842dd65987 --- /dev/null +++ b/queue-7.0/asoc-sdw_utils-check-speaker-component-string-alloca.patch @@ -0,0 +1,73 @@ +From 3085b572d1d4d95f8d04d61b2548f0fb201263c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 11:03:53 -0300 +Subject: ASoC: sdw_utils: Check speaker component string allocation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Cássio Gabriel + +[ Upstream commit 5a30862dec5a70da0a9d259de3f87a7542cc95b2 ] + +devm_kasprintf() can fail while building the temporary speaker +component string. If that happens, spk_components is set to NULL, but +the current code can still pass it to strlen() on a later loop iteration +or after the loop when appending the speaker component list to +card->components. + +Use NULL to represent the initial "no speaker components" state, and +return -ENOMEM immediately if building spk_components fails. + +Fixes: 0f60ecffbfe3 ("ASoC: sdw_utils: generate combined spk components string") +Signed-off-by: Cássio Gabriel +Link: https://patch.msgid.link/20260512-asoc-sdw-utils-spk-components-alloc-v1-1-c9bbd6d2e123@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_utils.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index bf6629dd48860..1b897c8c2c2ca 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -928,7 +928,7 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) + struct asoc_sdw_codec_info *codec_info; + struct snd_soc_dai *dai; + struct sdw_slave *sdw_peripheral; +- const char *spk_components=""; ++ const char *spk_components = NULL; + int dai_index; + int ret; + int i; +@@ -1011,7 +1011,7 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) + else + component = codec_info->dais[dai_index].component_name; + +- if (strlen (spk_components) == 0) ++ if (!spk_components) + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, "%s", component); + else +@@ -1019,13 +1019,15 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, + "%s+%s", spk_components, component); ++ ++ if (!spk_components) ++ return -ENOMEM; + } + + codec_info->dais[dai_index].rtd_init_done = true; +- + } + +- if (strlen (spk_components) > 0) { ++ if (spk_components) { + /* Update card components for speaker components */ + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s", + card->components, spk_components); +-- +2.53.0 + diff --git a/queue-7.0/asoc-sdw_utils-cs42l43-allow-spk-component-names-to-.patch b/queue-7.0/asoc-sdw_utils-cs42l43-allow-spk-component-names-to-.patch new file mode 100644 index 0000000000..4f10b922d7 --- /dev/null +++ b/queue-7.0/asoc-sdw_utils-cs42l43-allow-spk-component-names-to-.patch @@ -0,0 +1,128 @@ +From 3c02e313be01f70a0ca13ef83c8b490b9ec114e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Apr 2026 12:48:17 +0100 +Subject: ASoC: sdw_utils: cs42l43: allow spk component names to be combined + +From: Maciej Strozek + +[ Upstream commit 87a3f5c8ac2096e9406ce2ed3bf5b9bc1589a92d ] + +Move handling of cs42l43-spk component string into SOF mechanism [1] +which will allow it to be aggregated with other speakers. +Likewise handle the cs35l56-bridge special case which should not be +combined to keep compatibility with UCM. + +Link: https://github.com/thesofproject/linux/pull/5445 [1] +Link: https://github.com/alsa-project/alsa-ucm-conf/pull/747 +Reviewed-by: Bard Liao +Signed-off-by: Maciej Strozek +Suggested-by: Aaron Ma +Tested-by: Aaron Ma +Link: https://patch.msgid.link/20260420114823.194226-1-mstrozek@opensource.cirrus.com +Signed-off-by: Mark Brown +Stable-dep-of: 5a30862dec5a ("ASoC: sdw_utils: Check speaker component string allocation") +Signed-off-by: Sasha Levin +--- + sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 6 ------ + sound/soc/sdw_utils/soc_sdw_cs42l43.c | 12 +----------- + sound/soc/sdw_utils/soc_sdw_utils.c | 20 ++++++++++++++++---- + 3 files changed, 17 insertions(+), 21 deletions(-) + +diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +index 2a7109d53cbe3..e0e32a279787c 100644 +--- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c ++++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +@@ -40,12 +40,6 @@ static int asoc_sdw_bridge_cs35l56_asp_init(struct snd_soc_pcm_runtime *rtd) + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, +- "%s spk:cs35l56-bridge", +- card->components); +- if (!card->components) +- return -ENOMEM; +- + ret = snd_soc_dapm_new_controls(dapm, bridge_widgets, + ARRAY_SIZE(bridge_widgets)); + if (ret) { +diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c b/sound/soc/sdw_utils/soc_sdw_cs42l43.c +index 4a451b9d4f137..e99ea3c4e5dde 100644 +--- a/sound/soc/sdw_utils/soc_sdw_cs42l43.c ++++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c +@@ -107,21 +107,11 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS"); + + int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) + { +- struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; ++ struct snd_soc_component *component = dai->component; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); +- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + int ret; + +- if (!(ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)) { +- /* Will be set by the bridge code in this case */ +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, +- "%s spk:cs42l43-spk", +- card->components); +- if (!card->components) +- return -ENOMEM; +- } +- + ret = snd_soc_limit_volume(card, "cs42l43 Speaker Digital Volume", + CS42L43_SPK_VOLUME_0DB); + if (ret) +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +index f54043e5ff450..bf6629dd48860 100644 +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -713,6 +713,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { + { + .direction = {true, false}, + .codec_name = "cs42l43-codec", ++ .component_name = "cs42l43-spk", + .dai_name = "cs42l43-dp6", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, +@@ -922,6 +923,7 @@ static int asoc_sdw_find_codec_info_dai_index(const struct asoc_sdw_codec_info * + int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) + { + struct snd_soc_card *card = rtd->card; ++ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); + struct asoc_sdw_codec_info *codec_info; + struct snd_soc_dai *dai; +@@ -997,16 +999,26 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) + /* Generate the spk component string for card->components string */ + if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP && + codec_info->dais[dai_index].component_name) { ++ const char *component; ++ ++ /* ++ * For the special case of cs42l43 with sidecar amps, use only ++ * "cs35l56-bridge" as the component name in card->components ++ */ ++ if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS && ++ !strcmp(codec_info->dais[dai_index].component_name, "cs42l43-spk")) ++ component = "cs35l56-bridge"; ++ else ++ component = codec_info->dais[dai_index].component_name; ++ + if (strlen (spk_components) == 0) + spk_components = +- devm_kasprintf(card->dev, GFP_KERNEL, "%s", +- codec_info->dais[dai_index].component_name); ++ devm_kasprintf(card->dev, GFP_KERNEL, "%s", component); + else + /* Append component name to spk_components */ + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, +- "%s+%s", spk_components, +- codec_info->dais[dai_index].component_name); ++ "%s+%s", spk_components, component); + } + + codec_info->dais[dai_index].rtd_init_done = true; +-- +2.53.0 + diff --git a/queue-7.0/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch b/queue-7.0/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch new file mode 100644 index 0000000000..ddd0d0e33d --- /dev/null +++ b/queue-7.0/asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch @@ -0,0 +1,39 @@ +From 907de6b654c2321de5ed0042b842a98250b5bdda Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 12:40:24 +0700 +Subject: ASoC: soc-utils: Add missing va_end in snd_soc_ret() + +From: Robertus Diawan Chris + +[ Upstream commit 298a43b54432fbc3a32949a94c72544ee18c8c00 ] + +The default case in snd_soc_ret() use va_start without va_end to +cleanup "args" object which can cause undefined behavior. So, add +missing va_end to cleanup "args" object. + +This is reported by Coverity Scan as "Missing varargs init or cleanup". + +Fixes: 943116ba2a6a ("ASoC: add common snd_soc_ret() and use it") +Signed-off-by: Robertus Diawan Chris +Link: https://patch.msgid.link/20260519054024.274741-1-robertusdchris@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/soc-utils.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c +index c8adfff826bd4..9cb7567e263eb 100644 +--- a/sound/soc/soc-utils.c ++++ b/sound/soc/soc-utils.c +@@ -36,6 +36,7 @@ int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...) + vaf.va = &args; + + dev_err(dev, "ASoC error (%d): %pV", ret, &vaf); ++ va_end(args); + } + + return ret; +-- +2.53.0 + diff --git a/queue-7.0/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch b/queue-7.0/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch new file mode 100644 index 0000000000..82a52fe39d --- /dev/null +++ b/queue-7.0/asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch @@ -0,0 +1,47 @@ +From 94ea25ad954cf93e0ccd28b941c4d9068db7881e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:36:36 -0500 +Subject: ASoC: SOF: amd: Fix error code handling in psp_send_cmd() + +From: Mario Limonciello + +[ Upstream commit 2c7b1227e582e88db7917412dca4e752c1aff691 ] + +The smn_read_register() helper returns negative error codes on failure +or the register value on success. When used with read_poll_timeout(), +the return value is stored in the 'data' variable. + +Currently 'data' is declared as u32, which causes negative error codes +to be cast to large positive values. This makes the condition 'data > 0' +incorrectly treat errors as success. + +Fix by changing 'data' from u32 to int, matching the pattern used in +psp_mbox_ready() which correctly handles the same helper function. + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/linux-sound/agGES8vWrLOrBu28@stanley.mountain/ +Fixes: f120cf33d232 ("ASoC: SOF: amd: Use AMD_NODE") +Signed-off-by: Mario Limonciello +Link: https://patch.msgid.link/20260511153638.724810-1-mario.limonciello@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sof/amd/acp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +index 71a18f156de23..f615b8d1c8020 100644 +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -223,7 +223,7 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) + { + struct snd_sof_dev *sdev = adata->dev; + int ret; +- u32 data; ++ int data; + + if (!cmd) + return -EINVAL; +-- +2.53.0 + diff --git a/queue-7.0/blk-mq-pop-cached-request-if-it-is-usable.patch b/queue-7.0/blk-mq-pop-cached-request-if-it-is-usable.patch new file mode 100644 index 0000000000..2088796bd9 --- /dev/null +++ b/queue-7.0/blk-mq-pop-cached-request-if-it-is-usable.patch @@ -0,0 +1,119 @@ +From c7bd6219f7abebfee6c57351ed6aa238a8cb229b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 12:02:53 -0700 +Subject: blk-mq: pop cached request if it is usable + +From: Keith Busch + +[ Upstream commit dc278e9bf2b9513a763353e6b9cc21e0f532954e ] + +When submitting a bio to blk-mq, if the task should sleep after peeking +a cached request, but before it pops it, the plug flushes and calls +blk_mq_free_plug_rqs, freeing the cached_rqs. This creates a +use-after-free bug. Fix this by popping the cached request before any +possible blocking calls if it is suitable for use. + +Popping this request first holds a queue reference, so avoid any +serialization races with queue freezes and can safely proceed with +dispatching that request to the driver. This potentially increases a +timing window from when a driver wants to freeze its queue to when +requests stop being dispatched. That scenario is off the fast path +though, and drivers need to appropriately handle requests during a +freeze request anyway. + +The downside is the popped element needs to be individually freed when +we performed a bio plug merge. The cached request would have had to be +freed later anyway, but this patch does it inline with building the plug +list instead of after flushing it. + +Fixes: b0077e269f6c1 ("blk-mq: make sure active queue usage is held for bio_integrity_prep()") +Fixes: 7b4f36cd22a65 ("block: ensure we hold a queue reference when using queue limits") +Signed-off-by: Keith Busch +Link: https://patch.msgid.link/20260521190253.242065-1-kbusch@meta.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-mq.c | 34 +++++++++------------------------- + 1 file changed, 9 insertions(+), 25 deletions(-) + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 7a7d8d536841d..39986a742b981 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -3077,7 +3077,7 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, + /* + * Check if there is a suitable cached request and return it. + */ +-static struct request *blk_mq_peek_cached_request(struct blk_plug *plug, ++static struct request *blk_mq_get_cached_request(struct blk_plug *plug, + struct request_queue *q, blk_opf_t opf) + { + enum hctx_type type = blk_mq_get_hctx_type(opf); +@@ -3093,27 +3093,10 @@ static struct request *blk_mq_peek_cached_request(struct blk_plug *plug, + return NULL; + if (op_is_flush(rq->cmd_flags) != op_is_flush(opf)) + return NULL; ++ rq_list_pop(&plug->cached_rqs); + return rq; + } + +-static void blk_mq_use_cached_rq(struct request *rq, struct blk_plug *plug, +- struct bio *bio) +-{ +- if (rq_list_pop(&plug->cached_rqs) != rq) +- WARN_ON_ONCE(1); +- +- /* +- * If any qos ->throttle() end up blocking, we will have flushed the +- * plug and hence killed the cached_rq list as well. Pop this entry +- * before we throttle. +- */ +- rq_qos_throttle(rq->q, bio); +- +- blk_mq_rq_time_init(rq, blk_time_get_ns()); +- rq->cmd_flags = bio->bi_opf; +- INIT_LIST_HEAD(&rq->queuelist); +-} +- + static bool bio_unaligned(const struct bio *bio, struct request_queue *q) + { + unsigned int bs_mask = queue_logical_block_size(q) - 1; +@@ -3151,7 +3134,7 @@ void blk_mq_submit_bio(struct bio *bio) + /* + * If the plug has a cached request for this queue, try to use it. + */ +- rq = blk_mq_peek_cached_request(plug, q, bio->bi_opf); ++ rq = blk_mq_get_cached_request(plug, q, bio->bi_opf); + + /* + * A BIO that was released from a zone write plug has already been +@@ -3209,7 +3192,10 @@ void blk_mq_submit_bio(struct bio *bio) + + new_request: + if (rq) { +- blk_mq_use_cached_rq(rq, plug, bio); ++ rq_qos_throttle(rq->q, bio); ++ blk_mq_rq_time_init(rq, blk_time_get_ns()); ++ rq->cmd_flags = bio->bi_opf; ++ INIT_LIST_HEAD(&rq->queuelist); + } else { + rq = blk_mq_get_new_requests(q, plug, bio); + if (unlikely(!rq)) { +@@ -3255,12 +3241,10 @@ void blk_mq_submit_bio(struct bio *bio) + return; + + queue_exit: +- /* +- * Don't drop the queue reference if we were trying to use a cached +- * request and thus didn't acquire one. +- */ + if (!rq) + blk_queue_exit(q); ++ else ++ blk_mq_free_request(rq); + } + + #ifdef CONFIG_BLK_MQ_STACKING +-- +2.53.0 + diff --git a/queue-7.0/block-allow-submitting-all-zone-writes-from-a-single.patch b/queue-7.0/block-allow-submitting-all-zone-writes-from-a-single.patch new file mode 100644 index 0000000000..64921d6a5b --- /dev/null +++ b/queue-7.0/block-allow-submitting-all-zone-writes-from-a-single.patch @@ -0,0 +1,643 @@ +From 14f2647cf484a268ad282678449f2d5152271a81 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Feb 2026 22:19:49 +0900 +Subject: block: allow submitting all zone writes from a single context + +From: Damien Le Moal + +[ Upstream commit 1365b6904fd050bf22ab9f3df375a396de5837a1 ] + +In order to maintain sequential write patterns per zone with zoned block +devices, zone write plugging issues only a single write BIO per zone at +any time. This works well but has the side effect that when large +sequential write streams are issued by the user and these streams cross +zone boundaries, the device ends up receiving a discontiguous set of +write commands for different zones. The same also happens when a user +writes simultaneously at high queue depth multiple zones: the device +does not see all sequential writes per zone and receives discontiguous +writes to different zones. While this does not affect the performance of +solid state zoned block devices, when using an SMR HDD, this pattern +change from sequential writes to discontiguous writes to different zones +significantly increases head seek which results in degraded write +throughput. + +In order to reduce this seek overhead for rotational media devices, +introduce a per disk zone write plugs kernel thread to issue all write +BIOs to zones. This single zone write issuing context is enabled for +any zoned block device that has a request queue flagged with the new +QUEUE_ZONED_QD1_WRITES flag. + +The flag QUEUE_ZONED_QD1_WRITES is visible as the sysfs queue attribute +zoned_qd1_writes for zoned devices. For regular block devices, this +attribute is not visible. For zoned block devices, a user can override +the default value set to force the global write maximum queue depth of +1 for a zoned block device, or clear this attribute to fallback to the +default behavior of zone write plugging which limits writes to QD=1 per +sequential zone. + +Writing to a zoned block device flagged with QUEUE_ZONED_QD1_WRITES is +implemented using a list of zone write plugs that have a non-empty BIO +list. Listed zone write plugs are processed by the disk zone write plugs +worker kthread in FIFO order, and all BIOs of a zone write plug are all +processed before switching to the next listed zone write plug. A newly +submitted BIO for a non-FULL zone write plug that is not yet listed +causes the addition of the zone write plug at the end of the disk list +of zone write plugs. + +Since the write BIOs queued in a zone write plug BIO list are +necessarilly sequential, for rotational media, using the single zone +write plugs kthread to issue all BIOs maintains a sequential write +pattern and thus reduces seek overhead and improves write throughput. +This processing essentially result in always writing to HDDs at QD=1, +which is not an issue for HDDs operating with write caching enabled. +Performance with write cache disabled is also not degraded thanks to +the efficient write handling of modern SMR HDDs. + +A disk list of zone write plugs is defined using the new struct gendisk +zone_wplugs_list, and accesses to this list is protected using the +zone_wplugs_list_lock spinlock. The per disk kthread +(zone_wplugs_worker) code is implemented by the function +disk_zone_wplugs_worker(). A reference on listed zone write plugs is +always held until all BIOs of the zone write plug are processed by the +worker kthread. BIO issuing at QD=1 is driven using a completion +structure (zone_wplugs_worker_bio_done) and calls to blk_io_wait(). + +With this change, performance when sequentially writing the zones of a +30 TB SMR SATA HDD connected to an AHCI adapter changes as follows +(1MiB direct I/Os, results in MB/s unit): + + +--------------------+ + | Write BW (MB/s) | + +------------------+----------+---------+ + | Sequential write | Baseline | Patched | + | Queue Depth | 6.19-rc8 | | + +------------------+----------+---------+ + | 1 | 244 | 245 | + | 2 | 244 | 245 | + | 4 | 245 | 245 | + | 8 | 242 | 245 | + | 16 | 222 | 246 | + | 32 | 211 | 245 | + | 64 | 193 | 244 | + | 128 | 112 | 246 | + +------------------+----------+---------+ + +With the current code (baseline), as the sequential write stream crosses +a zone boundary, higher queue depth creates a gap between the +last IO to the previous zone and the first IOs to the following zones, +causing head seeks and degrading performance. Using the disk zone +write plugs worker thread, this pattern disappears and the maximum +throughput of the drive is maintained, leading to over 100% +improvements in throughput for high queue depth write. + +Using 16 fio jobs all writing to randomly chosen zones at QD=32 with 1 +MiB direct IOs, write throughput also increases significantly. + + +--------------------+ + | Write BW (MB/s) | + +------------------+----------+---------+ + | Random write | Baseline | Patched | + | Number of zones | 6.19-rc7 | | + +------------------+----------+---------+ + | 1 | 191 | 192 | + | 2 | 101 | 128 | + | 4 | 115 | 123 | + | 8 | 90 | 120 | + | 16 | 64 | 115 | + | 32 | 58 | 105 | + | 64 | 56 | 101 | + | 128 | 55 | 99 | + +------------------+----------+---------+ + +Tests using XFS shows that buffered write speed with 8 jobs writing +files increases by 12% to 35% depending on the workload. + + +--------------------+ + | Write BW (MB/s) | + +------------------+----------+---------+ + | Workload | Baseline | Patched | + | | 6.19-rc7 | | + +------------------+----------+---------+ + | 256MiB file size | 212 | 238 | + +------------------+----------+---------+ + | 4MiB .. 128 MiB | 213 | 243 | + | random file size | | | + +------------------+----------+---------+ + | 2MiB .. 8 MiB | 179 | 242 | + | random file size | | | + +------------------+----------+---------+ + +Performance gains are even more significant when using an HBA that +limits the maximum size of commands to a small value, e.g. HBAs +controlled with the mpi3mr driver limit commands to a maximum of 1 MiB. +In such case, the write throughput gains are over 40%. + + +--------------------+ + | Write BW (MB/s) | + +------------------+----------+---------+ + | Workload | Baseline | Patched | + | | 6.19-rc7 | | + +------------------+----------+---------+ + | 256MiB file size | 175 | 245 | + +------------------+----------+---------+ + | 4MiB .. 128 MiB | 174 | 244 | + | random file size | | | + +------------------+----------+---------+ + | 2MiB .. 8 MiB | 171 | 243 | + | random file size | | | + +------------------+----------+---------+ + +Signed-off-by: Damien Le Moal +Reviewed-by: Christoph Hellwig +Reviewed-by: Bart Van Assche +Reviewed-by: Johannes Thumshirn +Signed-off-by: Jens Axboe +Stable-dep-of: 836efd35c472 ("block: fix handling of dead zone write plugs") +Signed-off-by: Sasha Levin +--- + block/blk-mq-debugfs.c | 1 + + block/blk-sysfs.c | 35 +++++++- + block/blk-zoned.c | 190 ++++++++++++++++++++++++++++++++++++----- + include/linux/blkdev.h | 8 ++ + 4 files changed, 212 insertions(+), 22 deletions(-) + +diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c +index 28167c9baa559..047ec887456b6 100644 +--- a/block/blk-mq-debugfs.c ++++ b/block/blk-mq-debugfs.c +@@ -97,6 +97,7 @@ static const char *const blk_queue_flag_name[] = { + QUEUE_FLAG_NAME(NO_ELV_SWITCH), + QUEUE_FLAG_NAME(QOS_ENABLED), + QUEUE_FLAG_NAME(BIO_ISSUE_TIME), ++ QUEUE_FLAG_NAME(ZONED_QD1_WRITES), + }; + #undef QUEUE_FLAG_NAME + +diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c +index 55a1bbfef7d45..ca8033e6d6990 100644 +--- a/block/blk-sysfs.c ++++ b/block/blk-sysfs.c +@@ -390,6 +390,36 @@ static ssize_t queue_nr_zones_show(struct gendisk *disk, char *page) + return queue_var_show(disk_nr_zones(disk), page); + } + ++static ssize_t queue_zoned_qd1_writes_show(struct gendisk *disk, char *page) ++{ ++ return queue_var_show(!!blk_queue_zoned_qd1_writes(disk->queue), ++ page); ++} ++ ++static ssize_t queue_zoned_qd1_writes_store(struct gendisk *disk, ++ const char *page, size_t count) ++{ ++ struct request_queue *q = disk->queue; ++ unsigned long qd1_writes; ++ unsigned int memflags; ++ ssize_t ret; ++ ++ ret = queue_var_store(&qd1_writes, page, count); ++ if (ret < 0) ++ return ret; ++ ++ memflags = blk_mq_freeze_queue(q); ++ blk_mq_quiesce_queue(q); ++ if (qd1_writes) ++ blk_queue_flag_set(QUEUE_FLAG_ZONED_QD1_WRITES, q); ++ else ++ blk_queue_flag_clear(QUEUE_FLAG_ZONED_QD1_WRITES, q); ++ blk_mq_unquiesce_queue(q); ++ blk_mq_unfreeze_queue(q, memflags); ++ ++ return count; ++} ++ + static ssize_t queue_iostats_passthrough_show(struct gendisk *disk, char *page) + { + return queue_var_show(!!blk_queue_passthrough_stat(disk->queue), page); +@@ -617,6 +647,7 @@ QUEUE_LIM_RO_ENTRY(queue_max_zone_append_sectors, "zone_append_max_bytes"); + QUEUE_LIM_RO_ENTRY(queue_zone_write_granularity, "zone_write_granularity"); + + QUEUE_LIM_RO_ENTRY(queue_zoned, "zoned"); ++QUEUE_RW_ENTRY(queue_zoned_qd1_writes, "zoned_qd1_writes"); + QUEUE_RO_ENTRY(queue_nr_zones, "nr_zones"); + QUEUE_LIM_RO_ENTRY(queue_max_open_zones, "max_open_zones"); + QUEUE_LIM_RO_ENTRY(queue_max_active_zones, "max_active_zones"); +@@ -754,6 +785,7 @@ static struct attribute *queue_attrs[] = { + &queue_nomerges_entry.attr, + &queue_poll_entry.attr, + &queue_poll_delay_entry.attr, ++ &queue_zoned_qd1_writes_entry.attr, + + NULL, + }; +@@ -786,7 +818,8 @@ static umode_t queue_attr_visible(struct kobject *kobj, struct attribute *attr, + struct request_queue *q = disk->queue; + + if ((attr == &queue_max_open_zones_entry.attr || +- attr == &queue_max_active_zones_entry.attr) && ++ attr == &queue_max_active_zones_entry.attr || ++ attr == &queue_zoned_qd1_writes_entry.attr) && + !blk_queue_is_zoned(q)) + return 0; + +diff --git a/block/blk-zoned.c b/block/blk-zoned.c +index 2fa7f7b5f4c80..9b697043871f8 100644 +--- a/block/blk-zoned.c ++++ b/block/blk-zoned.c +@@ -16,6 +16,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -40,6 +42,8 @@ static const char *const zone_cond_name[] = { + /* + * Per-zone write plug. + * @node: hlist_node structure for managing the plug using a hash table. ++ * @entry: list_head structure for listing the plug in the disk list of active ++ * zone write plugs. + * @bio_list: The list of BIOs that are currently plugged. + * @bio_work: Work struct to handle issuing of plugged BIOs + * @rcu_head: RCU head to free zone write plugs with an RCU grace period. +@@ -62,6 +66,7 @@ static const char *const zone_cond_name[] = { + */ + struct blk_zone_wplug { + struct hlist_node node; ++ struct list_head entry; + struct bio_list bio_list; + struct work_struct bio_work; + struct rcu_head rcu_head; +@@ -629,7 +634,19 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug) + } + } + +-static void blk_zone_wplug_bio_work(struct work_struct *work); ++static bool disk_zone_wplug_submit_bio(struct gendisk *disk, ++ struct blk_zone_wplug *zwplug); ++ ++static void blk_zone_wplug_bio_work(struct work_struct *work) ++{ ++ struct blk_zone_wplug *zwplug = ++ container_of(work, struct blk_zone_wplug, bio_work); ++ ++ disk_zone_wplug_submit_bio(zwplug->disk, zwplug); ++ ++ /* Drop the reference we took in disk_zone_wplug_schedule_work(). */ ++ disk_put_zone_wplug(zwplug); ++} + + /* + * Get a reference on the write plug for the zone containing @sector. +@@ -667,6 +684,7 @@ static struct blk_zone_wplug *disk_get_and_lock_zone_wplug(struct gendisk *disk, + zwplug->wp_offset = bdev_offset_from_zone_start(disk->part0, sector); + bio_list_init(&zwplug->bio_list); + INIT_WORK(&zwplug->bio_work, blk_zone_wplug_bio_work); ++ INIT_LIST_HEAD(&zwplug->entry); + zwplug->disk = disk; + + spin_lock_irqsave(&zwplug->lock, *flags); +@@ -702,6 +720,7 @@ static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug, + */ + static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug) + { ++ struct gendisk *disk = zwplug->disk; + struct bio *bio; + + lockdep_assert_held(&zwplug->lock); +@@ -715,6 +734,20 @@ static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug) + blk_zone_wplug_bio_io_error(zwplug, bio); + + zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; ++ ++ /* ++ * If we are using the per disk zone write plugs worker thread, remove ++ * the zone write plug from the work list and drop the reference we ++ * took when the zone write plug was added to that list. ++ */ ++ if (blk_queue_zoned_qd1_writes(disk->queue)) { ++ spin_lock(&disk->zone_wplugs_list_lock); ++ if (!list_empty(&zwplug->entry)) { ++ list_del_init(&zwplug->entry); ++ disk_put_zone_wplug(zwplug); ++ } ++ spin_unlock(&disk->zone_wplugs_list_lock); ++ } + } + + /* +@@ -1149,8 +1182,8 @@ void blk_zone_mgmt_bio_endio(struct bio *bio) + } + } + +-static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk, +- struct blk_zone_wplug *zwplug) ++static void disk_zone_wplug_schedule_work(struct gendisk *disk, ++ struct blk_zone_wplug *zwplug) + { + lockdep_assert_held(&zwplug->lock); + +@@ -1163,6 +1196,7 @@ static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk, + * and we also drop this reference if the work is already scheduled. + */ + WARN_ON_ONCE(!(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED)); ++ WARN_ON_ONCE(blk_queue_zoned_qd1_writes(disk->queue)); + refcount_inc(&zwplug->ref); + if (!queue_work(disk->zone_wplugs_wq, &zwplug->bio_work)) + disk_put_zone_wplug(zwplug); +@@ -1202,6 +1236,22 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk, + bio_list_add(&zwplug->bio_list, bio); + trace_disk_zone_wplug_add_bio(zwplug->disk->queue, zwplug->zone_no, + bio->bi_iter.bi_sector, bio_sectors(bio)); ++ ++ /* ++ * If we are using the disk zone write plugs worker instead of the per ++ * zone write plug BIO work, add the zone write plug to the work list ++ * if it is not already there. Make sure to also get an extra reference ++ * on the zone write plug so that it does not go away until it is ++ * removed from the work list. ++ */ ++ if (blk_queue_zoned_qd1_writes(disk->queue)) { ++ spin_lock(&disk->zone_wplugs_list_lock); ++ if (list_empty(&zwplug->entry)) { ++ list_add_tail(&zwplug->entry, &disk->zone_wplugs_list); ++ refcount_inc(&zwplug->ref); ++ } ++ spin_unlock(&disk->zone_wplugs_list_lock); ++ } + } + + /* +@@ -1433,6 +1483,13 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs) + goto queue_bio; + } + ++ /* ++ * For rotational devices, we will use the gendisk zone write plugs ++ * work instead of the per zone write plug BIO work, so queue the BIO. ++ */ ++ if (blk_queue_zoned_qd1_writes(disk->queue)) ++ goto queue_bio; ++ + /* If the zone is already plugged, add the BIO to the BIO plug list. */ + if (zwplug->flags & BLK_ZONE_WPLUG_PLUGGED) + goto queue_bio; +@@ -1455,7 +1512,10 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs) + + if (!(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED)) { + zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED; +- disk_zone_wplug_schedule_bio_work(disk, zwplug); ++ if (blk_queue_zoned_qd1_writes(disk->queue)) ++ wake_up_process(disk->zone_wplugs_worker); ++ else ++ disk_zone_wplug_schedule_work(disk, zwplug); + } + + spin_unlock_irqrestore(&zwplug->lock, flags); +@@ -1596,16 +1656,22 @@ static void disk_zone_wplug_unplug_bio(struct gendisk *disk, + + spin_lock_irqsave(&zwplug->lock, flags); + +- /* Schedule submission of the next plugged BIO if we have one. */ +- if (!bio_list_empty(&zwplug->bio_list)) { +- disk_zone_wplug_schedule_bio_work(disk, zwplug); +- spin_unlock_irqrestore(&zwplug->lock, flags); +- return; +- } ++ /* ++ * For rotational devices, signal the BIO completion to the zone write ++ * plug work. Otherwise, schedule submission of the next plugged BIO ++ * if we have one. ++ */ ++ if (bio_list_empty(&zwplug->bio_list)) ++ zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; ++ ++ if (blk_queue_zoned_qd1_writes(disk->queue)) ++ complete(&disk->zone_wplugs_worker_bio_done); ++ else if (!bio_list_empty(&zwplug->bio_list)) ++ disk_zone_wplug_schedule_work(disk, zwplug); + +- zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; + if (!zwplug->wp_offset || disk_zone_wplug_is_full(disk, zwplug)) + disk_mark_zone_wplug_dead(zwplug); ++ + spin_unlock_irqrestore(&zwplug->lock, flags); + } + +@@ -1695,10 +1761,9 @@ void blk_zone_write_plug_finish_request(struct request *req) + disk_put_zone_wplug(zwplug); + } + +-static void blk_zone_wplug_bio_work(struct work_struct *work) ++static bool disk_zone_wplug_submit_bio(struct gendisk *disk, ++ struct blk_zone_wplug *zwplug) + { +- struct blk_zone_wplug *zwplug = +- container_of(work, struct blk_zone_wplug, bio_work); + struct block_device *bdev; + unsigned long flags; + struct bio *bio; +@@ -1714,7 +1779,7 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) + if (!bio) { + zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED; + spin_unlock_irqrestore(&zwplug->lock, flags); +- goto put_zwplug; ++ return false; + } + + trace_blk_zone_wplug_bio(zwplug->disk->queue, zwplug->zone_no, +@@ -1728,14 +1793,15 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) + goto again; + } + +- bdev = bio->bi_bdev; +- + /* + * blk-mq devices will reuse the extra reference on the request queue + * usage counter we took when the BIO was plugged, but the submission + * path for BIO-based devices will not do that. So drop this extra + * reference here. + */ ++ if (blk_queue_zoned_qd1_writes(disk->queue)) ++ reinit_completion(&disk->zone_wplugs_worker_bio_done); ++ bdev = bio->bi_bdev; + if (bdev_test_flag(bdev, BD_HAS_SUBMIT_BIO)) { + bdev->bd_disk->fops->submit_bio(bio); + blk_queue_exit(bdev->bd_disk->queue); +@@ -1743,14 +1809,78 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) + blk_mq_submit_bio(bio); + } + +-put_zwplug: +- /* Drop the reference we took in disk_zone_wplug_schedule_bio_work(). */ +- disk_put_zone_wplug(zwplug); ++ return true; ++} ++ ++static struct blk_zone_wplug *disk_get_zone_wplugs_work(struct gendisk *disk) ++{ ++ struct blk_zone_wplug *zwplug; ++ ++ spin_lock_irq(&disk->zone_wplugs_list_lock); ++ zwplug = list_first_entry_or_null(&disk->zone_wplugs_list, ++ struct blk_zone_wplug, entry); ++ if (zwplug) ++ list_del_init(&zwplug->entry); ++ spin_unlock_irq(&disk->zone_wplugs_list_lock); ++ ++ return zwplug; ++} ++ ++static int disk_zone_wplugs_worker(void *data) ++{ ++ struct gendisk *disk = data; ++ struct blk_zone_wplug *zwplug; ++ unsigned int noio_flag; ++ ++ noio_flag = memalloc_noio_save(); ++ set_user_nice(current, MIN_NICE); ++ set_freezable(); ++ ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE | TASK_FREEZABLE); ++ ++ zwplug = disk_get_zone_wplugs_work(disk); ++ if (zwplug) { ++ /* ++ * Process all BIOs of this zone write plug and then ++ * drop the reference we took when adding the zone write ++ * plug to the active list. ++ */ ++ set_current_state(TASK_RUNNING); ++ while (disk_zone_wplug_submit_bio(disk, zwplug)) ++ blk_wait_io(&disk->zone_wplugs_worker_bio_done); ++ disk_put_zone_wplug(zwplug); ++ continue; ++ } ++ ++ /* ++ * Only sleep if nothing sets the state to running. Else check ++ * for zone write plugs work again as a newly submitted BIO ++ * might have added a zone write plug to the work list. ++ */ ++ if (get_current_state() == TASK_RUNNING) { ++ try_to_freeze(); ++ } else { ++ if (kthread_should_stop()) { ++ set_current_state(TASK_RUNNING); ++ break; ++ } ++ schedule(); ++ } ++ } ++ ++ WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list)); ++ memalloc_noio_restore(noio_flag); ++ ++ return 0; + } + + void disk_init_zone_resources(struct gendisk *disk) + { + spin_lock_init(&disk->zone_wplugs_hash_lock); ++ spin_lock_init(&disk->zone_wplugs_list_lock); ++ INIT_LIST_HEAD(&disk->zone_wplugs_list); ++ init_completion(&disk->zone_wplugs_worker_bio_done); + } + + /* +@@ -1766,6 +1896,7 @@ static int disk_alloc_zone_resources(struct gendisk *disk, + unsigned int pool_size) + { + unsigned int i; ++ int ret = -ENOMEM; + + atomic_set(&disk->nr_zone_wplugs, 0); + disk->zone_wplugs_hash_bits = +@@ -1791,8 +1922,21 @@ static int disk_alloc_zone_resources(struct gendisk *disk, + if (!disk->zone_wplugs_wq) + goto destroy_pool; + ++ disk->zone_wplugs_worker = ++ kthread_create(disk_zone_wplugs_worker, disk, ++ "%s_zwplugs_worker", disk->disk_name); ++ if (IS_ERR(disk->zone_wplugs_worker)) { ++ ret = PTR_ERR(disk->zone_wplugs_worker); ++ disk->zone_wplugs_worker = NULL; ++ goto destroy_wq; ++ } ++ wake_up_process(disk->zone_wplugs_worker); ++ + return 0; + ++destroy_wq: ++ destroy_workqueue(disk->zone_wplugs_wq); ++ disk->zone_wplugs_wq = NULL; + destroy_pool: + mempool_destroy(disk->zone_wplugs_pool); + disk->zone_wplugs_pool = NULL; +@@ -1800,7 +1944,7 @@ static int disk_alloc_zone_resources(struct gendisk *disk, + kfree(disk->zone_wplugs_hash); + disk->zone_wplugs_hash = NULL; + disk->zone_wplugs_hash_bits = 0; +- return -ENOMEM; ++ return ret; + } + + static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk) +@@ -1850,6 +1994,10 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond) + + void disk_free_zone_resources(struct gendisk *disk) + { ++ if (disk->zone_wplugs_worker) ++ kthread_stop(disk->zone_wplugs_worker); ++ WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list)); ++ + if (disk->zone_wplugs_wq) { + destroy_workqueue(disk->zone_wplugs_wq); + disk->zone_wplugs_wq = NULL; +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index 6890900237707..ac899cd0cd708 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -204,6 +205,10 @@ struct gendisk { + struct mempool *zone_wplugs_pool; + struct hlist_head *zone_wplugs_hash; + struct workqueue_struct *zone_wplugs_wq; ++ spinlock_t zone_wplugs_list_lock; ++ struct list_head zone_wplugs_list; ++ struct task_struct *zone_wplugs_worker; ++ struct completion zone_wplugs_worker_bio_done; + #endif /* CONFIG_BLK_DEV_ZONED */ + + #if IS_ENABLED(CONFIG_CDROM) +@@ -668,6 +673,7 @@ enum { + QUEUE_FLAG_NO_ELV_SWITCH, /* can't switch elevator any more */ + QUEUE_FLAG_QOS_ENABLED, /* qos is enabled */ + QUEUE_FLAG_BIO_ISSUE_TIME, /* record bio->issue_time_ns */ ++ QUEUE_FLAG_ZONED_QD1_WRITES, /* Limit zoned devices writes to QD=1 */ + QUEUE_FLAG_MAX + }; + +@@ -707,6 +713,8 @@ void blk_queue_flag_clear(unsigned int flag, struct request_queue *q); + test_bit(QUEUE_FLAG_DISABLE_WBT_DEF, &(q)->queue_flags) + #define blk_queue_no_elv_switch(q) \ + test_bit(QUEUE_FLAG_NO_ELV_SWITCH, &(q)->queue_flags) ++#define blk_queue_zoned_qd1_writes(q) \ ++ test_bit(QUEUE_FLAG_ZONED_QD1_WRITES, &(q)->queue_flags) + + extern void blk_set_pm_only(struct request_queue *q); + extern void blk_clear_pm_only(struct request_queue *q); +-- +2.53.0 + diff --git a/queue-7.0/block-avoid-use-after-free-in-disk_free_zone_resourc.patch b/queue-7.0/block-avoid-use-after-free-in-disk_free_zone_resourc.patch new file mode 100644 index 0000000000..2f51345c49 --- /dev/null +++ b/queue-7.0/block-avoid-use-after-free-in-disk_free_zone_resourc.patch @@ -0,0 +1,64 @@ +From bcd0522e6a6a7ddf761021bf7a795f5f19e392aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 20:56:22 +0900 +Subject: block: avoid use-after-free in disk_free_zone_resources() + +From: Damien Le Moal + +[ Upstream commit f6982769910ecddabdb5b8b9afdab0bb8b6668ac ] + +The function disk_update_zone_resources() may call +disk_free_zone_resources() in case of error, and following this, +blk_revalidate_disk_zones() will again calls disk_free_zone_resources() if +disk_update_zone_resources() failed. If a zone worker thread is being used +(which is the default for a rotational media zoned device), +disk_free_zone_resources() will try to stop the zone worker thread twice +because disk->zone_wplugs_worker is not reset to NULL when the worker +thread is stopped the first time. + +In disk_free_zone_resources(), fix this by correctly clearing +disk->zone_wplugs_worker to NULL when the worker thread is stopped. + +And while at it, since disk_free_zone_resources() is always called after a +failed call to disk_update_zone_resources(), remove the unnecessary call +to disk_free_zone_resources() in disk_update_zone_resources(). + +Fixes: 1365b6904fd0 ("block: allow submitting all zone writes from a single context") +Signed-off-by: Damien Le Moal +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260522115622.588535-1-dlemoal@kernel.org +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-zoned.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/block/blk-zoned.c b/block/blk-zoned.c +index af724ce650801..fe29fe4b6dccc 100644 +--- a/block/blk-zoned.c ++++ b/block/blk-zoned.c +@@ -2016,8 +2016,10 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond) + + void disk_free_zone_resources(struct gendisk *disk) + { +- if (disk->zone_wplugs_worker) ++ if (disk->zone_wplugs_worker) { + kthread_stop(disk->zone_wplugs_worker); ++ disk->zone_wplugs_worker = NULL; ++ } + WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list)); + + if (disk->zone_wplugs_wq) { +@@ -2150,9 +2152,6 @@ static int disk_update_zone_resources(struct gendisk *disk, + ret = queue_limits_commit_update(q, &lim); + + unfreeze: +- if (ret) +- disk_free_zone_resources(disk); +- + blk_mq_unfreeze_queue(q, memflags); + + return ret; +-- +2.53.0 + diff --git a/queue-7.0/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch b/queue-7.0/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch new file mode 100644 index 0000000000..7b796604f6 --- /dev/null +++ b/queue-7.0/block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch @@ -0,0 +1,72 @@ +From ab3a34ddfd365fcc98d114478f40ead3b2190460 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 01:09:29 -0400 +Subject: block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user() + +From: Sungwoo Kim + +[ Upstream commit 8582792cf23b3d94674d4d838f7cde9a28d0fcaf ] + +pin_user_pages_fast() can partially succeed and return the number of +pages that were actually pinned. However, the bio_integrity_map_user() +does not handle this partial pinning. This leads to a general protection +fault since bvec_from_pages() dereferences an unpinned page address, +which is 0. + +To fix this, add a check to verify that all requested memory is pinned. +If partial pinning occurs, unpin the memory and return -EFAULT. + +Kernel Oops: + +Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy) +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 +RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6 + +Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers") +Acked-by: Chao Shi +Acked-by: Weidong Zhu +Acked-by: Dave Tian +Signed-off-by: Sungwoo Kim +Tested-by: Shin'ichiro Kawasaki +Link: https://github.com/linux-blktests/blktests/pull/244 +Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index 5a316d8bc5efa..d6c9a09a8dc6e 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -338,6 +338,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) + if (unlikely(ret < 0)) + goto free_bvec; + ++ /* ++ * Handle partial pinning. This can happen when pin_user_pages_fast() ++ * returns fewer pages than requested. ++ */ ++ if (user_backed_iter(iter) && unlikely(ret != bytes)) { ++ if (ret > 0) { ++ int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ int i; ++ ++ for (i = 0; i < npinned; i++) ++ unpin_user_page(pages[i]); ++ } ++ if (pages != stack_pages) ++ kvfree(pages); ++ ret = -EFAULT; ++ goto free_bvec; ++ } ++ + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, + &is_p2p); + if (pages != stack_pages) +-- +2.53.0 + diff --git a/queue-7.0/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch b/queue-7.0/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch new file mode 100644 index 0000000000..b049db54e7 --- /dev/null +++ b/queue-7.0/block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch @@ -0,0 +1,43 @@ +From 78fa36da7ce3c22d53d97634acf9a34ea01292fb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 22:51:51 +0100 +Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user() + +From: David Carlier + +[ Upstream commit 637ad3a56a3b889527d1dacea6fea2a8bd648140 ] + +bio_integrity_add_page() already sets bip_vcnt to 1 for the bounce +segment. Overwriting it with nr_vecs breaks bip_vcnt <= bip_max_vcnt +on WRITE (bip_max_vcnt is 1), so the gap-merge checks in block/blk.h +read past the bip_vec[] flex array. On READ the read is in bounds +but lands on a saved user bvec instead of the bounce. + +The line was added for split propagation, but bio_integrity_clone() +doesn't copy bip_vcnt and BIP_CLONE_FLAGS excludes BIP_COPY_USER. + +Fixes: 3991657ae707 ("block: set bip_vcnt correctly") +Signed-off-by: David Carlier +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bio-integrity.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/block/bio-integrity.c b/block/bio-integrity.c +index a319362217037..5a316d8bc5efa 100644 +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -244,7 +244,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, + } + + bip->bip_flags |= BIP_COPY_USER; +- bip->bip_vcnt = nr_vecs; + return 0; + free_bip: + bio_integrity_free(bio); +-- +2.53.0 + diff --git a/queue-7.0/block-fix-handling-of-dead-zone-write-plugs.patch b/queue-7.0/block-fix-handling-of-dead-zone-write-plugs.patch new file mode 100644 index 0000000000..7f9591bb99 --- /dev/null +++ b/queue-7.0/block-fix-handling-of-dead-zone-write-plugs.patch @@ -0,0 +1,104 @@ +From 25f916dac4195fac69edba27f6e352bc414a9197 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 20:11:29 +0900 +Subject: block: fix handling of dead zone write plugs + +From: Damien Le Moal + +[ Upstream commit 836efd35c472d89c838d7b17ef339ddb3286ffc5 ] + +Shin'ichiro reported hard to reproduce unaligned write errors with zoned +block devices. Under normal operation conditions (e.g. running XFS on an +SMR disk), these errors are nearly impossible to trigger. But using a +"slow" kernel with many debug options enables and some specific use +cases (e.g. fio zbd test case 46), the errors can be reproduced fairly +easily. + +The unaligned write errors come from mishandling a valid reference +counting pattern of zone write plugs. Such pattern triggers for instance +if a process A writes a zone (not necessarilly to the full state), +another process B immediately resets the zone and immediately following +the completion of the zone reset, starts issuing writes to the zone. +With such pattern, in some cases, the zone write plugs worker thread of +the device may still be holding a reference to the zone write plug of +the zone taken when process A was writing to the zone. The following +zone reset from process B marks the zone as dead but does not remove the +zone write plug from the device hash table as a reference to the plug +still exist. Once process B starts issuing new writes, the zone write +plug is seen as dead and the writes from process B are immediately +failed, despite this write pattern being perfectly legal. + +Fix this by allowing restoring a dead zone write plug to a live state if +a write is issued to the zone when the zone is: marked as dead, empty +and the write sector corresponds to the first sector of the zone (that +is, the write is aligned to the zone write pointer). This is done with +the new helper function disk_check_zone_wplug_dead(), which restores a +dead zone write plug to a live state by clearing the BLK_ZONE_WPLUG_DEAD +flag and restoring the initial reference to the zone write plug taken +when the plug was added to the device hash table. + +Reported-by: Shin'ichiro Kawasaki +Fixes: b7d4ffb51037 ("block: fix zone write plug removal") +Signed-off-by: Damien Le Moal +Tested-by: Shin'ichiro Kawasaki +Link: https://patch.msgid.link/20260513111129.108809-1-dlemoal@kernel.org +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-zoned.c | 32 +++++++++++++++++++++++++++----- + 1 file changed, 27 insertions(+), 5 deletions(-) + +diff --git a/block/blk-zoned.c b/block/blk-zoned.c +index 9b697043871f8..af724ce650801 100644 +--- a/block/blk-zoned.c ++++ b/block/blk-zoned.c +@@ -634,6 +634,28 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug) + } + } + ++static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug) ++{ ++ if (!(zwplug->flags & BLK_ZONE_WPLUG_DEAD)) ++ return false; ++ ++ /* ++ * If a new write is received right after a zone reset completes and ++ * while the disk_zone_wplugs_worker() thread has not yet released the ++ * reference on the zone write plug after processing the last write to ++ * the zone, then the new write BIO will see the zone write plug marked ++ * as dead. This case is however a false positive and a perfectly valid ++ * pattern. In such case, restore the zone write plug to a live one. ++ */ ++ if (!zwplug->wp_offset && bio_list_empty(&zwplug->bio_list)) { ++ zwplug->flags &= ~BLK_ZONE_WPLUG_DEAD; ++ refcount_inc(&zwplug->ref); ++ return false; ++ } ++ ++ return true; ++} ++ + static bool disk_zone_wplug_submit_bio(struct gendisk *disk, + struct blk_zone_wplug *zwplug); + +@@ -1459,12 +1481,12 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs) + } + + /* +- * If we got a zone write plug marked as dead, then the user is issuing +- * writes to a full zone, or without synchronizing with zone reset or +- * zone finish operations. In such case, fail the BIO to signal this +- * invalid usage. ++ * Check if we got a zone write plug marked as dead. If yes, then the ++ * user is likely issuing writes to a full zone, or without ++ * synchronizing with zone reset or zone finish operations. In such ++ * case, fail the BIO to signal this invalid usage. + */ +- if (zwplug->flags & BLK_ZONE_WPLUG_DEAD) { ++ if (disk_check_zone_wplug_dead(zwplug)) { + spin_unlock_irqrestore(&zwplug->lock, flags); + disk_put_zone_wplug(zwplug); + bio_io_error(bio); +-- +2.53.0 + diff --git a/queue-7.0/block-recompute-nr_integrity_segments-in-blk_insert_.patch b/queue-7.0/block-recompute-nr_integrity_segments-in-blk_insert_.patch new file mode 100644 index 0000000000..2b52585891 --- /dev/null +++ b/queue-7.0/block-recompute-nr_integrity_segments-in-blk_insert_.patch @@ -0,0 +1,82 @@ +From bb9b4aa6f52a1c4e5bd4463e4374bfa3a58c8323 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:22:30 -0600 +Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request + +From: Casey Chen + +[ Upstream commit 2c6e6a18a37b905cb584eb0dda3ae482162a81ca ] + +blk_insert_cloned_request() already recomputes nr_phys_segments +against the bottom queue, because "the queue settings related to +segment counting may differ from the original queue." The exact same +reasoning applies to integrity segments: a stacked driver's underlying +queue can have tighter virt_boundary_mask, seg_boundary_mask, or +max_segment_size than the top queue, in which case +blk_rq_count_integrity_sg() against the bottom queue produces a +different count than the cached rq->nr_integrity_segments inherited +from the source request by blk_rq_prep_clone(). + +When the cached count is lower than the bottom queue's actual count, +blk_rq_map_integrity_sg() trips + + BUG_ON(segments > rq->nr_integrity_segments); + +on dispatch. The same families of stacked setups that motivated the +existing nr_phys_segments recompute -- dm-multipath fanning out to +nvme-rdma in particular -- can produce this. + +Mirror the nr_phys_segments handling: when the request carries +integrity, recompute nr_integrity_segments against the bottom queue +and reject the request if it exceeds the bottom queue's +max_integrity_segments. blk_rq_count_integrity_sg() and +queue_max_integrity_segments() are both already available via +, which blk-mq.c includes. + +This closes a latent gap in the stacking contract and brings the +integrity-segment accounting in line with the existing +phys-segment accounting. + +Fixes: 76c313f658d2 ("blk-integrity: improved sg segment mapping") +Signed-off-by: Casey Chen +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/blk-mq.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 3da2215b29125..7a7d8d536841d 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -3305,6 +3305,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq) + return BLK_STS_IOERR; + } + ++ /* ++ * Integrity segment counting depends on the same queue limits ++ * (virt_boundary_mask, seg_boundary_mask, max_segment_size) that ++ * vary across stacked queues, so recompute against the bottom ++ * queue just like nr_phys_segments above. ++ */ ++ if (blk_integrity_rq(rq) && rq->bio) { ++ unsigned short max_int_segs = queue_max_integrity_segments(q); ++ ++ rq->nr_integrity_segments = ++ blk_rq_count_integrity_sg(rq->q, rq->bio); ++ if (rq->nr_integrity_segments > max_int_segs) { ++ printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n", ++ __func__, rq->nr_integrity_segments, ++ max_int_segs); ++ return BLK_STS_IOERR; ++ } ++ } ++ + if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq))) + return BLK_STS_IOERR; + +-- +2.53.0 + diff --git a/queue-7.0/block-rename-struct-gendisk-zone_wplugs_lock-field.patch b/queue-7.0/block-rename-struct-gendisk-zone_wplugs_lock-field.patch new file mode 100644 index 0000000000..c2e412347c --- /dev/null +++ b/queue-7.0/block-rename-struct-gendisk-zone_wplugs_lock-field.patch @@ -0,0 +1,118 @@ +From f5226eac4619db88a81295a657a72387808f10b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Feb 2026 22:19:48 +0900 +Subject: block: rename struct gendisk zone_wplugs_lock field + +From: Damien Le Moal + +[ Upstream commit b7cbc30e93e3a64ea058230f6d0c764d6d80276f ] + +Rename struct gendisk zone_wplugs_lock field to zone_wplugs_hash_lock to +clearly indicates that this is the spinlock used for manipulating the +hash table of zone write plugs. + +Signed-off-by: Damien Le Moal +Reviewed-by: Hannes Reinecke +Reviewed-by: Johannes Thumshirn +Reviewed-by: Christoph Hellwig +Reviewed-by: Bart Van Assche +Signed-off-by: Jens Axboe +Stable-dep-of: 836efd35c472 ("block: fix handling of dead zone write plugs") +Signed-off-by: Sasha Levin +--- + block/blk-zoned.c | 23 ++++++++++++----------- + include/linux/blkdev.h | 2 +- + 2 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/block/blk-zoned.c b/block/blk-zoned.c +index a4d82342e37ac..2fa7f7b5f4c80 100644 +--- a/block/blk-zoned.c ++++ b/block/blk-zoned.c +@@ -520,10 +520,11 @@ static bool disk_insert_zone_wplug(struct gendisk *disk, + * are racing with other submission context, so we may already have a + * zone write plug for the same zone. + */ +- spin_lock_irqsave(&disk->zone_wplugs_lock, flags); ++ spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags); + hlist_for_each_entry_rcu(zwplg, &disk->zone_wplugs_hash[idx], node) { + if (zwplg->zone_no == zwplug->zone_no) { +- spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags); ++ spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, ++ flags); + return false; + } + } +@@ -535,7 +536,7 @@ static bool disk_insert_zone_wplug(struct gendisk *disk, + * necessarilly in the active condition. + */ + zones_cond = rcu_dereference_check(disk->zones_cond, +- lockdep_is_held(&disk->zone_wplugs_lock)); ++ lockdep_is_held(&disk->zone_wplugs_hash_lock)); + if (zones_cond) + zwplug->cond = zones_cond[zwplug->zone_no]; + else +@@ -543,7 +544,7 @@ static bool disk_insert_zone_wplug(struct gendisk *disk, + + hlist_add_head_rcu(&zwplug->node, &disk->zone_wplugs_hash[idx]); + atomic_inc(&disk->nr_zone_wplugs); +- spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags); ++ spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags); + + return true; + } +@@ -596,13 +597,13 @@ static void disk_free_zone_wplug(struct blk_zone_wplug *zwplug) + WARN_ON_ONCE(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED); + WARN_ON_ONCE(!bio_list_empty(&zwplug->bio_list)); + +- spin_lock_irqsave(&disk->zone_wplugs_lock, flags); ++ spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags); + blk_zone_set_cond(rcu_dereference_check(disk->zones_cond, +- lockdep_is_held(&disk->zone_wplugs_lock)), ++ lockdep_is_held(&disk->zone_wplugs_hash_lock)), + zwplug->zone_no, zwplug->cond); + hlist_del_init_rcu(&zwplug->node); + atomic_dec(&disk->nr_zone_wplugs); +- spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags); ++ spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags); + + call_rcu(&zwplug->rcu_head, disk_free_zone_wplug_rcu); + } +@@ -1749,7 +1750,7 @@ static void blk_zone_wplug_bio_work(struct work_struct *work) + + void disk_init_zone_resources(struct gendisk *disk) + { +- spin_lock_init(&disk->zone_wplugs_lock); ++ spin_lock_init(&disk->zone_wplugs_hash_lock); + } + + /* +@@ -1839,10 +1840,10 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond) + { + unsigned long flags; + +- spin_lock_irqsave(&disk->zone_wplugs_lock, flags); ++ spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags); + zones_cond = rcu_replace_pointer(disk->zones_cond, zones_cond, +- lockdep_is_held(&disk->zone_wplugs_lock)); +- spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags); ++ lockdep_is_held(&disk->zone_wplugs_hash_lock)); ++ spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags); + + kfree_rcu_mightsleep(zones_cond); + } +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index d463b9b5a0a59..6890900237707 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -200,7 +200,7 @@ struct gendisk { + u8 __rcu *zones_cond; + unsigned int zone_wplugs_hash_bits; + atomic_t nr_zone_wplugs; +- spinlock_t zone_wplugs_lock; ++ spinlock_t zone_wplugs_hash_lock; + struct mempool *zone_wplugs_pool; + struct hlist_head *zone_wplugs_hash; + struct workqueue_struct *zone_wplugs_wq; +-- +2.53.0 + diff --git a/queue-7.0/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch b/queue-7.0/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch new file mode 100644 index 0000000000..c04eb9a961 --- /dev/null +++ b/queue-7.0/bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch @@ -0,0 +1,82 @@ +From b135de4c20354f19cc4ab50af4e3fdb40852c3c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 00:32:48 +0530 +Subject: Bluetooth: btintel_pcie: Fix incorrect MAC access programming + +From: Kiran K + +[ Upstream commit 88365d04fdc821dc4e9eb0cc00fdf6905430d172 ] + +btintel_pcie_get_mac_access() and btintel_pcie_release_mac_access() +were programming STOP_MAC_ACCESS_DIS and XTAL_CLK_REQ in addition to +the MAC_ACCESS_REQ handshake. These bits are not part of the host +MAC-access handshake on the supported parts; the driver was +programming them incorrectly. Drop the writes so the register update +contains only the bits the controller actually consumes. + +Fixes: b9465e6670a2 ("Bluetooth: btintel_pcie: Read hardware exception data") +Signed-off-by: Kiran K +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel_pcie.c | 20 ++++++-------------- + drivers/bluetooth/btintel_pcie.h | 3 --- + 2 files changed, 6 insertions(+), 17 deletions(-) + +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +index 37b744e35bc45..8dbd72895cb23 100644 +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -568,12 +568,10 @@ static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data) + + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); + +- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; +- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; +- if ((reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS) == 0) ++ if (!(reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ)) { + reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; +- +- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ } + + do { + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); +@@ -593,16 +591,10 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data) + + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); + +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) ++ if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) { + reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; +- +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS) +- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; +- +- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ) +- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; +- +- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); ++ } + } + + static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type, +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +index e3d941ffef4aa..34aa092bfbe33 100644 +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -34,9 +34,6 @@ + #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20)) + + #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21)) +-/* Stop MAC Access disconnection request */ +-#define BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS (BIT(22)) +-#define BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ (BIT(23)) + + #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS (BIT(28)) + #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29)) +-- +2.53.0 + diff --git a/queue-7.0/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch b/queue-7.0/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch new file mode 100644 index 0000000000..9307fbe205 --- /dev/null +++ b/queue-7.0/bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch @@ -0,0 +1,43 @@ +From 97f7f8af0d073186fac1815b748f3b0c4bca63e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:24:02 +0800 +Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths + +From: Jiajia Liu + +[ Upstream commit dd1dda6b8d6e1f4376a5b3055a04f0ecbdb4d6bd ] + +The setup_packet of control urb is not freed if usb_submit_urb fails or +the submitted urb is killed. Add free in these two paths. + +Fixes: a1c49c434e150 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") +Signed-off-by: Jiajia Liu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btmtk.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c +index a4b4dacfd2ad3..04f183fd3d12b 100644 +--- a/drivers/bluetooth/btmtk.c ++++ b/drivers/bluetooth/btmtk.c +@@ -496,6 +496,7 @@ static void btmtk_usb_wmt_recv(struct urb *urb) + return; + } else if (urb->status == -ENOENT) { + /* Avoid suspend failed when usb_kill_urb */ ++ kfree(urb->setup_packet); + return; + } + +@@ -569,6 +570,7 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); ++ kfree(dr); + usb_unanchor_urb(urb); + } + +-- +2.53.0 + diff --git a/queue-7.0/bluetooth-hci_sync-fix-not-setting-mask-for-hci_evt_.patch b/queue-7.0/bluetooth-hci_sync-fix-not-setting-mask-for-hci_evt_.patch new file mode 100644 index 0000000000..ac33a23f04 --- /dev/null +++ b/queue-7.0/bluetooth-hci_sync-fix-not-setting-mask-for-hci_evt_.patch @@ -0,0 +1,51 @@ +From b6dce8c7e4351353582a8a037581165d4cba24b8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 09:42:24 -0400 +Subject: Bluetooth: hci_sync: Fix not setting mask for + HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE + +From: Luiz Augusto von Dentz + +[ Upstream commit 23d528d817a485fe9800a66c9411bd9e3d8a6f63 ] + +This fixes not setting the bit for HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE +when extended features bit is set otherwise the controller may not +generate HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE causing +hci_le_read_all_remote_features_sync to timeout waiting for it. + +Also remove dead code. + +Fixes: a106e50be74b ("Bluetooth: HCI: Add support for LL Extended Feature Set") +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 919ec275dd237..426f465be3553 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -4438,6 +4438,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) + events[4] |= 0x02; /* LE BIG Info Advertising Report */ + } + ++ if (ll_ext_feature_capable(hdev)) ++ events[5] |= BIT(2); ++ + if (le_cs_capable(hdev)) { + /* Channel Sounding events */ + events[5] |= 0x08; /* LE CS Read Remote Supported Cap Complete event */ +@@ -7413,9 +7416,6 @@ static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev, + sizeof(cp), &cp, + HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE, + HCI_CMD_TIMEOUT, NULL); +- +- return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES, +- sizeof(cp), &cp, HCI_CMD_TIMEOUT); + } + + static int hci_le_read_remote_features_sync(struct hci_dev *hdev, void *data) +-- +2.53.0 + diff --git a/queue-7.0/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch b/queue-7.0/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch new file mode 100644 index 0000000000..b26415565c --- /dev/null +++ b/queue-7.0/bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch @@ -0,0 +1,82 @@ +From 5fa2822d11c4b4d5f059ca6b89d068eacccaab1e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 23:56:26 +0900 +Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx + +From: Xingwang Xiang + +[ Upstream commit ddf8029623a1af20e984c040e89ff918158397ab ] + +sk_psock_strp_data_ready() already checks tls_sw_has_ctx_rx() and +defers to psock->saved_data_ready when a TLS RX context is present, +avoiding a conflict with the TLS strparser's ownership of the receive +queue (commit e91de6afa81c, "bpf: Fix running sk_skb program types +with ktls"). + +sk_psock_verdict_data_ready() has no equivalent guard. When a socket +is inserted into a sockmap (BPF_SK_SKB_VERDICT) before TLS RX is +configured, tls_sw_strparser_arm() saves sk_psock_verdict_data_ready +as rx_ctx->saved_data_ready. On data arrival: + + tls_data_ready -> tls_strp_data_ready -> tls_rx_msg_ready + -> saved_data_ready() = sk_psock_verdict_data_ready() + -> tcp_read_skb() drains sk_receive_queue via __skb_unlink() + without calling tcp_eat_skb(), so copied_seq is not advanced. + +tls_strp_msg_load() then finds tcp_inq() >= full_len (stale), calls +tcp_recv_skb() on the now-empty queue, hits WARN_ON_ONCE(!first), and +returns with rx_ctx->strp.anchor.frag_list pointing at a psock-owned +(potentially freed) skb. tls_decrypt_sg() subsequently walks that +frag_list: use-after-free. + +Apply the same fix as sk_psock_strp_data_ready(): if a TLS RX context +is present, call psock->saved_data_ready (sock_def_readable) to wake +recv() waiters and return immediately, leaving the receive queue +untouched. TLS retains sole ownership of the queue and decrypts the +record normally through tls_sw_recvmsg(). + +Fixes: ef5659280eb1 ("bpf, sockmap: Allow skipping sk_skb parser program") +Signed-off-by: Xingwang Xiang +Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/skmsg.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/core/skmsg.c b/net/core/skmsg.c +index 6187a83bd7411..e1850caf1a71a 100644 +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -1268,12 +1268,19 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) + static void sk_psock_verdict_data_ready(struct sock *sk) + { + const struct proto_ops *ops = NULL; ++ struct sk_psock *psock; + struct socket *sock; + int copied; + + trace_sk_data_ready(sk); + + rcu_read_lock(); ++ psock = sk_psock(sk); ++ if (psock && tls_sw_has_ctx_rx(sk)) { ++ psock->saved_data_ready(sk); ++ rcu_read_unlock(); ++ return; ++ } + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); +@@ -1283,8 +1290,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) + + copied = ops->read_skb(sk, sk_psock_verdict_recv); + if (copied >= 0) { +- struct sk_psock *psock; +- + rcu_read_lock(); + psock = sk_psock(sk); + if (psock) +-- +2.53.0 + diff --git a/queue-7.0/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch b/queue-7.0/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch new file mode 100644 index 0000000000..e989129bd5 --- /dev/null +++ b/queue-7.0/bridge-mcast-fix-a-possible-use-after-free-when-remo.patch @@ -0,0 +1,134 @@ +From 7f8e47b2fcce55a7d891b9b2cb149dcf8b8928f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 15:11:21 +0300 +Subject: bridge: mcast: Fix a possible use-after-free when removing a bridge + port + +From: Ido Schimmel + +[ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] + +When per-VLAN multicast snooping is enabled, the bridge iterates over +all the bridge ports, disables the per-port multicast context on each +port and enables the per-{port, VLAN} multicast contexts instead. The +reverse happens when per-VLAN multicast snooping is disabled. + +When global multicast snooping is enabled, the bridge iterates over all +the bridge ports and enables the per-port multicast context on each +port. The reverse happens when multicast snooping is disabled. + +The above scheme can result in a situation where both types of contexts +(per-port and per-{port, VLAN}) are enabled on a single bridge port: + + # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 + # ip link add name dummy1 up master br1 type dummy + # ip link set dev br1 type bridge mcast_vlan_snooping 1 + # ip link set dev br1 type bridge mcast_snooping 0 + # ip link set dev br1 type bridge mcast_snooping 1 + +This is not intended and it is a problem since the commit cited below. +Prior to this commit, when removing a bridge port, +br_multicast_disable_port() would disable the per-port multicast context +and the per-{port, VLAN} multicast contexts would get disabled when +flushing VLANs. + +After this commit, br_multicast_disable_port() only disables the +per-port multicast context if per-VLAN multicast snooping is disabled. +If both types of contexts were enabled on the port when it was removed, +the per-port multicast context would remain enabled when freeing the +bridge port, leading to a use-after-free [1]. + +Fix by preventing the bridge from enabling / disabling the per-port +multicast contexts when toggling global multicast snooping if per-VLAN +multicast snooping is enabled. + +[1] +ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) +WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 +[...] +Call Trace: + +__debug_check_no_obj_freed (lib/debugobjects.c:1116) +kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) +kobject_cleanup (lib/kobject.c:689) +rcu_do_batch (kernel/rcu/tree.c:2617) +rcu_core (kernel/rcu/tree.c:2869) +handle_softirqs (kernel/softirq.c:622) +__irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) +irq_exit_rcu (kernel/softirq.c:752) +sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) + + +Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") +Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ +Reported-by: Thomas Gleixner +Acked-by: Nikolay Aleksandrov +Signed-off-by: Ido Schimmel +Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_multicast.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 881d866d687a0..2eef4f3345cd7 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -4640,10 +4640,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, + rcu_read_unlock(); + } + +-static void br_multicast_del_grps(struct net_bridge *br) ++static void br_multicast_enable_all_ports(struct net_bridge *br) + { + struct net_bridge_port *port; + ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ ++ list_for_each_entry(port, &br->port_list, list) ++ __br_multicast_enable_port_ctx(&port->multicast_ctx); ++} ++ ++static void br_multicast_disable_all_ports(struct net_bridge *br) ++{ ++ struct net_bridge_port *port; ++ ++ if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) ++ return; ++ + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); + } +@@ -4651,7 +4665,6 @@ static void br_multicast_del_grps(struct net_bridge *br) + int br_multicast_toggle(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack) + { +- struct net_bridge_port *port; + bool change_snoopers = false; + int err = 0; + +@@ -4668,7 +4681,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { + change_snoopers = true; +- br_multicast_del_grps(br); ++ br_multicast_disable_all_ports(br); + goto unlock; + } + +@@ -4676,8 +4689,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, + goto unlock; + + br_multicast_open(br); +- list_for_each_entry(port, &br->port_list, list) +- __br_multicast_enable_port_ctx(&port->multicast_ctx); ++ br_multicast_enable_all_ports(br); + + change_snoopers = true; + +-- +2.53.0 + diff --git a/queue-7.0/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch b/queue-7.0/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch new file mode 100644 index 0000000000..f8bf3cc910 --- /dev/null +++ b/queue-7.0/btrfs-check-for-subvolume-before-deleting-squota-qgr.patch @@ -0,0 +1,130 @@ +From 4bc372a66435b33fc25a85c7ada2fb8411bcf75e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 13:07:11 -0700 +Subject: btrfs: check for subvolume before deleting squota qgroup + +From: Boris Burkov + +[ Upstream commit 1e92637722ae4bd417f7a37e8d1485dc23b93935 ] + +The invariant that we want to maintain with subvolume qgroups is that +the qgroup can only be deleted if there is no root. With squotas, we +thought that it was sufficient to just check the usage, because we +assumed that deleting a subvolume will drive it's qgroups usage to 0, +and thus 0 usage implies no subvolume. + +However, this is false, for two reasons: + +- A subvol whose extents are all from before squotas was enabled. +- A subvol that was created in this transaction and for which we have + not yet run any delayed refs. + +In both cases, deleting the qgroup breaks the desired invariant and we +are left with a subvolume with no qgroup but squotas are enabled. + +Fix this by unifying the deletion check logic between full qgroups and +squotas. Squotas do all the same checks *and* the additional usage == 0 +check, which is the one extra rule peculiar to squotas. + +Link: https://lore.kernel.org/linux-btrfs/adnBhWfJQ1n3hZC8@merlins.org/ +Fixes: a8df35619948 ("btrfs: forbid deleting live subvol qgroup") +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 50 +++++++++++++++++++++++------------------------ + 1 file changed, 25 insertions(+), 25 deletions(-) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 41589ce663718..5204263088921 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1715,32 +1715,24 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) + return ret; + } + +-static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) +- ++static bool can_delete_parent_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) + { + ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); ++ if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ++ squota_check_parent_usage(fs_info, qgroup); + return list_empty(&qgroup->members); + } + + /* +- * Return true if we can delete the squota qgroup and false otherwise. +- * +- * Rules for whether we can delete: +- * +- * A subvolume qgroup can be removed iff the subvolume is fully deleted, which +- * is iff there is 0 usage in the qgroup. +- * +- * A higher level qgroup can be removed iff it has no members. +- * Note: We audit its usage to warn on inconsitencies without blocking deletion. ++ * Because a shared extent can outlive its owning subvolume, we cannot delete a ++ * subvol squota qgroup until all of the extents it owns are gone, even if the ++ * subvolume itself has been deleted. + */ +-static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) ++static bool can_delete_squota_subvol_qgroup(struct btrfs_fs_info *fs_info, ++ struct btrfs_qgroup *qgroup) + { + ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); +- +- if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { +- squota_check_parent_usage(fs_info, qgroup); +- return can_delete_parent_qgroup(qgroup); +- } ++ ASSERT(btrfs_qgroup_level(qgroup->qgroupid) == 0); + + return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); + } +@@ -1754,14 +1746,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + { + struct btrfs_key key; + BTRFS_PATH_AUTO_FREE(path); +- +- /* Since squotas cannot be inconsistent, they have special rules for deletion. */ +- if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) +- return can_delete_squota_qgroup(fs_info, qgroup); ++ int ret; + + /* For higher level qgroup, we can only delete it if it has no child. */ + if (btrfs_qgroup_level(qgroup->qgroupid)) +- return can_delete_parent_qgroup(qgroup); ++ return can_delete_parent_qgroup(fs_info, qgroup); + + /* + * For level-0 qgroups, we can only delete it if it has no subvolume +@@ -1777,10 +1766,21 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup + return -ENOMEM; + + /* +- * The @ret from btrfs_find_root() exactly matches our definition for +- * the return value, thus can be returned directly. ++ * Any subvol qgroup, regardless of mode, cannot be deleted if the ++ * subvol still exists. ++ */ ++ ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); ++ /* ++ * btrfs_find_root returns <0 on error, 0 if found, and >0 if not, ++ * so the "found" and "error" cases match our desired return values. + */ +- return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); ++ if (ret <= 0) ++ return ret; ++ ++ /* Squotas require additional checks, even if the subvol is deleted. */ ++ if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) ++ return can_delete_squota_subvol_qgroup(fs_info, qgroup); ++ return 1; + } + + int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) +-- +2.53.0 + diff --git a/queue-7.0/btrfs-fix-squota-accounting-during-enable-generation.patch b/queue-7.0/btrfs-fix-squota-accounting-during-enable-generation.patch new file mode 100644 index 0000000000..a36a59b72d --- /dev/null +++ b/queue-7.0/btrfs-fix-squota-accounting-during-enable-generation.patch @@ -0,0 +1,130 @@ +From 565bdb43305568d3192a951ad7e1b0c0c295c3c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 19:53:46 -0700 +Subject: btrfs: fix squota accounting during enable generation + +From: Boris Burkov + +[ Upstream commit d7c600554816b8ef70adffe078a0e360c055d82b ] + +The first transaction that enables squotas is special and a bit tricky. +We have to set BTRFS_FS_QUOTA_ENABLED after the transaction to avoid a +deadlock, so any delayed refs that run before we set the bit are not +squota accounted. For data this is fine, we don't get an owner_ref, so +there is no real harm, it's as if the extent predated squotas. However +for metadata, the tree block will have gen == enable_gen so when we free +it later, we will decrement the squota accounting, which can result in +an underflow. Before it is freed, btrfs check shows errors, as we have +mismatched usage between the node generations/owners and the squota +values. + +There are two angles to this fix: + +1. For extents that come in delayed_refs that run during the + enable_gen transaction, we must actually set enable_gen to the *next* + transaction. That is the first transaction that we can really + properly account in any way. +2. For extents that come in between the end of our transaction handle + and the time we set the BTRFS_FS_QUOTA_ENABLED bit, we need an + additional bit, BTRFS_FS_SQUOTA_ENABLING which only affects recording + squota deltas, so we do pick up those extents. Otherwise, we would + miss them, even for enable_gen + 1. + +Fixes: bd7c1ea3a302 ("btrfs: qgroup: check generation when recording simple quota delta") +Reviewed-by: Qu Wenruo +Signed-off-by: Boris Burkov +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/fs.h | 1 + + fs/btrfs/qgroup.c | 31 +++++++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 4 deletions(-) + +diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h +index 3de3b517810ed..d8c41f194729e 100644 +--- a/fs/btrfs/fs.h ++++ b/fs/btrfs/fs.h +@@ -154,6 +154,7 @@ enum { + BTRFS_FS_LOG_RECOVERING, + BTRFS_FS_OPEN, + BTRFS_FS_QUOTA_ENABLED, ++ BTRFS_FS_SQUOTA_ENABLING, + BTRFS_FS_UPDATE_UUID_TREE_GEN, + BTRFS_FS_CREATING_FREE_SPACE_TREE, + BTRFS_FS_BTREE_ERR, +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 5204263088921..0823f5f561d75 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1107,7 +1107,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + if (simple) { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE; + btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA); +- btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid); ++ /* ++ * Set the enable generation to the next transaction, as we cannot ++ * ensure that extents written during this transaction will see any ++ * state we have set here. So we should treat all extents of the ++ * transaction as coming in before squotas was enabled. ++ */ ++ btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1); + } else { + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; + } +@@ -1210,7 +1216,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + goto out_free_path; + } + +- fs_info->qgroup_enable_gen = trans->transid; ++ /* ++ * Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING ++ * under the transaction handle. We want to ensure that all extents in ++ * the next transaction definitely see them. ++ */ ++ if (simple) { ++ fs_info->qgroup_enable_gen = trans->transid + 1; ++ set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ } + + mutex_unlock(&fs_info->qgroup_ioctl_lock); + /* +@@ -1224,9 +1238,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + */ + ret = btrfs_commit_transaction(trans); + trans = NULL; ++ + mutex_lock(&fs_info->qgroup_ioctl_lock); +- if (ret) ++ if (ret) { ++ if (simple) { ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); ++ fs_info->qgroup_enable_gen = 0; ++ } + goto out_free_path; ++ } + + /* + * Set quota enabled flag after committing the transaction, to avoid +@@ -1236,6 +1256,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, + spin_lock(&fs_info->qgroup_lock); + fs_info->quota_root = quota_root; + set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); ++ if (simple) ++ clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); + spin_unlock(&fs_info->qgroup_lock); + + /* Skip rescan for simple qgroups. */ +@@ -4924,7 +4946,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info, + u64 num_bytes = delta->num_bytes; + const int sign = (delta->is_inc ? 1 : -1); + +- if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) ++ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE && ++ !test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags)) + return 0; + + if (!btrfs_is_fstree(root)) +-- +2.53.0 + diff --git a/queue-7.0/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch b/queue-7.0/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch new file mode 100644 index 0000000000..aa04b4ca9a --- /dev/null +++ b/queue-7.0/btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch @@ -0,0 +1,75 @@ +From 2dbab4a832da159be6065b08d96104b5e753691f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:58:56 +0100 +Subject: btrfs: tracepoints: fix sleep while in atomic context in + btrfs_sync_file() + +From: Filipe Manana + +[ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ] + +The trace event btrfs_sync_file() is called in an atomic context (all trace +events are) and its call to dput(), which is needed due to the call to +dget_parent(), can sleep, triggering a kernel splat. + +This can be reproduced by enabling the trace event and running btrfs/056 +from fstests for example. The splat shown in dmesg is the following: + + [53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 + [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io + [53.988] preempt_count: 2, expected: 0 + [53.967] RCU nest depth: 0, expected: 0 + [53.943] Preemption disabled at: + [53.944] [<0000000000000000>] 0x0 + [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) + [54.070] Tainted: [W]=WARN + [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 + [54.072] Call Trace: + [54.074] + [54.076] dump_stack_lvl+0x56/0x80 + [54.079] __might_resched.cold+0xd6/0x10f + [54.072] dput.part.0+0x24/0x110 + [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] + [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] + [54.087] ? __handle_mm_fault+0x8ae/0xed0 + [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] + [54.091] vfs_write+0x21f/0x450 + [54.094] __x64_sys_pwrite64+0x8d/0xc0 + [54.096] ? do_user_addr_fault+0x20c/0x670 + [54.099] do_syscall_64+0x60/0xf20 + [54.092] ? clear_bhb_loop+0x60/0xb0 + [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e + +So stop using dget_parent() and dput() and access the parent dentry +directly as dentry->d_parent. This is also what ext4 is doing in +its equivalent trace event ext4_sync_file_enter(). + +Fixes: a85b46db143f ("btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()") +Reviewed-by: Boris Burkov +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + include/trace/events/btrfs.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h +index 0864700f76e0a..fa090a455037a 100644 +--- a/include/trace/events/btrfs.h ++++ b/include/trace/events/btrfs.h +@@ -771,10 +771,8 @@ TRACE_EVENT(btrfs_sync_file, + TP_fast_assign( + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); +- struct dentry *parent = dget_parent(dentry); +- struct inode *parent_inode = d_inode(parent); ++ struct inode *parent_inode = d_inode(dentry->d_parent); + +- dput(parent); + TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); + __entry->ino = btrfs_ino(BTRFS_I(inode)); + __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); +-- +2.53.0 + diff --git a/queue-7.0/cachefiles-fix-error-return-when-vfs_mkdir-fails.patch b/queue-7.0/cachefiles-fix-error-return-when-vfs_mkdir-fails.patch new file mode 100644 index 0000000000..3a9e50155f --- /dev/null +++ b/queue-7.0/cachefiles-fix-error-return-when-vfs_mkdir-fails.patch @@ -0,0 +1,42 @@ +From 2a203624325f9f7bc9bc7d1981e57e45e4372820 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 18:34:06 +0800 +Subject: cachefiles: Fix error return when vfs_mkdir() fails + +From: Hongling Zeng + +[ Upstream commit 8a220d1c312c66194f4a33dd52d1fba42bc2b341 ] + +When vfs_mkdir() fails, the error code is not extracted from the +returned error pointer. This causes mkdir_error to be reached with +ret=0, which leads to returning ERR_PTR(0) (NULL) instead of a +proper error pointer. + +Fix this by extracting the error code from the error pointer when +vfs_mkdir() fails. + +Fixes: 406fad7698f5 ("cachefiles: Fix oops in vfs_mkdir from cachefiles_get_directory") +Signed-off-by: Hongling Zeng +Link: https://patch.msgid.link/20260513103406.202320-1-zenghongling@kylinos.cn +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/cachefiles/namei.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c +index eb9eb7683e3cc..6336d976d469b 100644 +--- a/fs/cachefiles/namei.c ++++ b/fs/cachefiles/namei.c +@@ -130,6 +130,8 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, + ret = cachefiles_inject_write_error(); + if (ret == 0) { + subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL); ++ if (IS_ERR(subdir)) ++ ret = PTR_ERR(subdir); + } else { + end_creating(subdir); + subdir = ERR_PTR(ret); +-- +2.53.0 + diff --git a/queue-7.0/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch b/queue-7.0/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch new file mode 100644 index 0000000000..430c55cfd7 --- /dev/null +++ b/queue-7.0/cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch @@ -0,0 +1,61 @@ +From caa94f6e9e5b1fbfe2a00e8f78348f47013745be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 11:30:54 +0800 +Subject: cgroup: rstat: relax NMI guard after switch to try_cmpxchg + +From: Cunlong Li + +[ Upstream commit 22572dbcd3486e6c4dced877125bbf50e4e24edf ] + +Commit 36df6e3dbd7e ("cgroup: make css_rstat_updated nmi safe") used +this_cpu_cmpxchg() for the lockless insertion, and therefore required +both ARCH_HAVE_NMI_SAFE_CMPXCHG and ARCH_HAS_NMI_SAFE_THIS_CPU_OPS in +the NMI guard: on archs without the latter, this_cpu_cmpxchg() falls +back to "local_irq_save() + plain cmpxchg", and local_irq_save() +cannot mask NMIs. + +Commit 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in +css_rstat_updated") later replaced this_cpu_cmpxchg() with plain +try_cmpxchg() to fix cross-CPU lockless-list corruption, but left the +NMI guard untouched. After that switch, css_rstat_updated() no longer +performs any this_cpu_*() RMW operations and only relies on the arch +having NMI-safe cmpxchg, so ARCH_HAS_NMI_SAFE_THIS_CPU_OPS is no +longer required in the guard. + +Relax the guard accordingly so that archs which have HAVE_NMI and +ARCH_HAVE_NMI_SAFE_CMPXCHG but not ARCH_HAS_NMI_SAFE_THIS_CPU_OPS +(e.g. sparc, powerpc on PPC64/BOOK3S) can benefit from the existing +CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC path. Without this, the css +is never queued in NMI on those archs, and the atomics staged by +account_{slab,kmem}_nmi_safe() are not drained by flush_nmi_stats(). + +Fixes: 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in css_rstat_updated") +Signed-off-by: Cunlong Li +Signed-off-by: Tejun Heo +Signed-off-by: Sasha Levin +--- + kernel/cgroup/rstat.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c +index ed60ba119c687..de816a43db9f0 100644 +--- a/kernel/cgroup/rstat.c ++++ b/kernel/cgroup/rstat.c +@@ -81,11 +81,10 @@ void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + lockdep_assert_preemption_disabled(); + + /* +- * For archs withnot nmi safe cmpxchg or percpu ops support, ignore +- * the requests from nmi context. ++ * The lockless insertion below relies on NMI-safe cmpxchg; ++ * bail out in NMI on archs that don't provide it. + */ +- if ((!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) || +- !IS_ENABLED(CONFIG_ARCH_HAS_NMI_SAFE_THIS_CPU_OPS)) && in_nmi()) ++ if (!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && in_nmi()) + return; + + rstatc = css_rstat_cpu(css, cpu); +-- +2.53.0 + diff --git a/queue-7.0/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch b/queue-7.0/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch new file mode 100644 index 0000000000..e1b2be118d --- /dev/null +++ b/queue-7.0/cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch @@ -0,0 +1,182 @@ +From 21dfa28d88cc7ed926678c7fb0a1f1948329c25e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 15:08:49 +0800 +Subject: cgroup/rstat: validate cpu before css_rstat_cpu() access + +From: Qing Ming + +[ Upstream commit 8817005efbdfdf5d4e4814cb5dc52b53d12917d7 ] + +css_rstat_updated() is exposed as a BPF kfunc and accepts a +caller-provided cpu argument. The function uses cpu for per-cpu rstat +lookups without checking whether it refers to a valid possible CPU. + +A BPF iter/cgroup program with CAP_BPF and CAP_PERFMON can pass an +invalid cpu value. On an unfixed UBSCAN_BOUNDS test kernel, cpu == +0x7fffffff triggers: + + UBSAN: array-index-out-of-bounds in kernel/cgroup/rstat.c:31:9 + index 2147483647 is out of range for type 'long unsigned int [64]' + Call Trace: + css_rstat_updated + bpf_iter_run_prog + cgroup_iter_seq_show + bpf_seq_read + +Add cpu validation to the BPF-facing css_rstat_updated() kfunc and +move the common implementation to __css_rstat_updated() for in-kernel +callers. + +Fixes: a319185be9f5 ("cgroup: bpf: enable bpf programs to integrate with rstat") +Signed-off-by: Qing Ming +Signed-off-by: Tejun Heo +Signed-off-by: Sasha Levin +--- + block/blk-cgroup.c | 2 +- + include/linux/cgroup.h | 1 + + kernel/cgroup/rstat.c | 30 ++++++++++++++++++++---------- + mm/memcontrol.c | 6 +++--- + 4 files changed, 25 insertions(+), 14 deletions(-) + +diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c +index 554c87bb4a865..bc63bd220865d 100644 +--- a/block/blk-cgroup.c ++++ b/block/blk-cgroup.c +@@ -2241,7 +2241,7 @@ void blk_cgroup_bio_start(struct bio *bio) + } + + u64_stats_update_end_irqrestore(&bis->sync, flags); +- css_rstat_updated(&blkcg->css, cpu); ++ __css_rstat_updated(&blkcg->css, cpu); + put_cpu(); + } + +diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h +index bc892e3b37eea..b61b9b7849df4 100644 +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -715,6 +715,7 @@ static inline void cgroup_path_from_kernfs_id(u64 id, char *buf, size_t buflen) + /* + * cgroup scalable recursive statistics. + */ ++void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu); + void css_rstat_updated(struct cgroup_subsys_state *css, int cpu); + void css_rstat_flush(struct cgroup_subsys_state *css); + +diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c +index 150e5871e66f2..ed60ba119c687 100644 +--- a/kernel/cgroup/rstat.c ++++ b/kernel/cgroup/rstat.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + #include "cgroup-internal.h" + ++#include + #include + + #include +@@ -53,7 +54,7 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) + } + + /** +- * css_rstat_updated - keep track of updated rstat_cpu ++ * __css_rstat_updated - keep track of updated rstat_cpu + * @css: target cgroup subsystem state + * @cpu: cpu on which rstat_cpu was updated + * +@@ -63,20 +64,17 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) + * + * NOTE: if the user needs the guarantee that the updater either add itself in + * the lockless list or the concurrent flusher flushes its updated stats, a +- * memory barrier is needed before the call to css_rstat_updated() i.e. a ++ * memory barrier is needed before the call to __css_rstat_updated() i.e. a + * barrier after updating the per-cpu stats and before calling +- * css_rstat_updated(). ++ * __css_rstat_updated(). + */ +-__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) ++void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + { + struct llist_head *lhead; + struct css_rstat_cpu *rstatc; + struct llist_node *self; + +- /* +- * Since bpf programs can call this function, prevent access to +- * uninitialized rstat pointers. +- */ ++ /* Prevent access to uninitialized rstat pointers. */ + if (!css_uses_rstat(css)) + return; + +@@ -125,6 +123,18 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) + llist_add(&rstatc->lnode, lhead); + } + ++/* ++ * BPF-facing wrapper for __css_rstat_updated(). Validate the caller-provided ++ * CPU before passing it to the internal rstat updater. ++ */ ++__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) ++{ ++ if (unlikely(cpu < 0 || cpu >= nr_cpu_ids || !cpu_possible(cpu))) ++ return; ++ ++ __css_rstat_updated(css, cpu); ++} ++ + static void __css_process_update_tree(struct cgroup_subsys_state *css, int cpu) + { + /* put @css and all ancestors on the corresponding updated lists */ +@@ -170,7 +180,7 @@ static void css_process_update_tree(struct cgroup_subsys *ss, int cpu) + * flusher flush the stats updated by the updater who have + * observed that they are already on the list. The + * corresponding barrier pair for this one should be before +- * css_rstat_updated() by the user. ++ * __css_rstat_updated() by the user. + * + * For now, there aren't any such user, so not adding the + * barrier here but if such a use-case arise, please add +@@ -614,7 +624,7 @@ static void cgroup_base_stat_cputime_account_end(struct cgroup *cgrp, + unsigned long flags) + { + u64_stats_update_end_irqrestore(&rstatbc->bsync, flags); +- css_rstat_updated(&cgrp->self, smp_processor_id()); ++ __css_rstat_updated(&cgrp->self, smp_processor_id()); + put_cpu_ptr(rstatbc); + } + +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index 772bac21d1558..96786a4af7533 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -574,7 +574,7 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, + if (!val) + return; + +- css_rstat_updated(&memcg->css, cpu); ++ __css_rstat_updated(&memcg->css, cpu); + statc_pcpu = memcg->vmstats_percpu; + for (; statc_pcpu; statc_pcpu = statc->parent_pcpu) { + statc = this_cpu_ptr(statc_pcpu); +@@ -2583,7 +2583,7 @@ static inline void account_slab_nmi_safe(struct mem_cgroup *memcg, + struct mem_cgroup_per_node *pn = memcg->nodeinfo[pgdat->node_id]; + + /* preemption is disabled in_nmi(). */ +- css_rstat_updated(&memcg->css, smp_processor_id()); ++ __css_rstat_updated(&memcg->css, smp_processor_id()); + if (idx == NR_SLAB_RECLAIMABLE_B) + atomic_add(nr, &pn->slab_reclaimable); + else +@@ -2807,7 +2807,7 @@ static inline void account_kmem_nmi_safe(struct mem_cgroup *memcg, int val) + mod_memcg_state(memcg, MEMCG_KMEM, val); + } else { + /* preemption is disabled in_nmi(). */ +- css_rstat_updated(&memcg->css, smp_processor_id()); ++ __css_rstat_updated(&memcg->css, smp_processor_id()); + atomic_add(val, &memcg->kmem_stat); + } + } +-- +2.53.0 + diff --git a/queue-7.0/cifs-client-stage-smb3_reconfigure-updates-and-resto.patch b/queue-7.0/cifs-client-stage-smb3_reconfigure-updates-and-resto.patch new file mode 100644 index 0000000000..01a085a29f --- /dev/null +++ b/queue-7.0/cifs-client-stage-smb3_reconfigure-updates-and-resto.patch @@ -0,0 +1,317 @@ +From 4566d27fd90d24602fd7c8e60c806325603bcfb5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 22:26:22 +0900 +Subject: cifs: client: stage smb3_reconfigure() updates and restore ctx on + failure + +From: DaeMyung Kang + +[ Upstream commit ab26dfeba278b0efbcea012f1698cf524d9b5695 ] + +smb3_reconfigure() moves strings out of cifs_sb->ctx before the +multichannel update, so a later failure can leave the live context +with NULL strings or options that do not match the session. + +Stage the new ctx separately, commit it only on success, and restore +the snapshot on failure. Also make smb3_sync_session_ctx_passwords() +all-or-nothing. + +Commit session passwords before channel updates so newly added channels +authenticate with the staged credentials. + +Fixes: ef529f655a2c ("cifs: client: allow changing multichannel mount options on remount") +Reported-by: RAJASI MANDAL +Closes: https://lore.kernel.org/lkml/CAEY6_V1+dzW3OD5zqXhsWyXwrDTrg5tAMGZ1AJ7_GAuRE+aevA@mail.gmail.com/ +Link: https://lore.kernel.org/lkml/xkr2dlvgibq5j6gkcxd3yhhnj4atgxw2uy4eug2pxm7wy7nbms@iq6cf5taa65v/ +Reviewed-by: Henrique Carvalho +Signed-off-by: DaeMyung Kang +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/fs_context.c | 161 +++++++++++++++++++++++++------------ + 1 file changed, 108 insertions(+), 53 deletions(-) + +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index a46764c247107..59598d334bae1 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -761,7 +761,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, + static int smb3_fs_context_parse_monolithic(struct fs_context *fc, + void *data); + static int smb3_get_tree(struct fs_context *fc); +-static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels); ++static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels); + static int smb3_reconfigure(struct fs_context *fc); + + static const struct fs_context_operations smb3_fs_context_ops = { +@@ -1035,25 +1035,34 @@ do { \ + + int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) + { ++ char *password = NULL, *password2 = NULL; ++ + if (ses->password && + cifs_sb->ctx->password && + strcmp(ses->password, cifs_sb->ctx->password)) { +- kfree_sensitive(cifs_sb->ctx->password); +- cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL); +- if (!cifs_sb->ctx->password) ++ password = kstrdup(ses->password, GFP_KERNEL); ++ if (!password) + return -ENOMEM; + } + if (ses->password2 && + cifs_sb->ctx->password2 && + strcmp(ses->password2, cifs_sb->ctx->password2)) { +- kfree_sensitive(cifs_sb->ctx->password2); +- cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL); +- if (!cifs_sb->ctx->password2) { +- kfree_sensitive(cifs_sb->ctx->password); +- cifs_sb->ctx->password = NULL; ++ password2 = kstrdup(ses->password2, GFP_KERNEL); ++ if (!password2) { ++ kfree_sensitive(password); + return -ENOMEM; + } + } ++ ++ if (password) { ++ kfree_sensitive(cifs_sb->ctx->password); ++ cifs_sb->ctx->password = password; ++ } ++ if (password2) { ++ kfree_sensitive(cifs_sb->ctx->password2); ++ cifs_sb->ctx->password2 = password2; ++ } ++ + return 0; + } + +@@ -1066,7 +1075,7 @@ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_se + * with the session's channel lock. This should be called whenever the maximum + * allowed channels for a session changes (e.g., after a remount or reconfigure). + */ +-static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels) ++static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels) + { + spin_lock(&ses->chan_lock); + ses->chan_max = max_channels; +@@ -1076,12 +1085,15 @@ static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channe + static int smb3_reconfigure(struct fs_context *fc) + { + struct smb3_fs_context *ctx = smb3_fc2context(fc); ++ struct smb3_fs_context *new_ctx = NULL; ++ struct smb3_fs_context *old_ctx = NULL; + struct dentry *root = fc->root; + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses; + unsigned int rsize = ctx->rsize, wsize = ctx->wsize; + char *new_password = NULL, *new_password2 = NULL; + bool need_recon = false; ++ bool need_mchan_update; + int rc; + + if (ses->expired_pwd) +@@ -1091,6 +1103,16 @@ static int smb3_reconfigure(struct fs_context *fc) + if (rc) + return rc; + ++ old_ctx = kzalloc_obj(*old_ctx); ++ if (!old_ctx) ++ return -ENOMEM; ++ ++ rc = smb3_fs_context_dup(old_ctx, cifs_sb->ctx); ++ if (rc) { ++ kfree(old_ctx); ++ return rc; ++ } ++ + /* + * We can not change UNC/username/password/domainname/ + * workstation_name/nodename/iocharset +@@ -1100,16 +1122,22 @@ static int smb3_reconfigure(struct fs_context *fc) + STEAL_STRING(cifs_sb, ctx, UNC); + STEAL_STRING(cifs_sb, ctx, source); + STEAL_STRING(cifs_sb, ctx, username); ++ STEAL_STRING(cifs_sb, ctx, domainname); ++ STEAL_STRING(cifs_sb, ctx, nodename); ++ STEAL_STRING(cifs_sb, ctx, iocharset); + +- if (need_recon == false) ++ if (!need_recon) { + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); +- else { ++ } else { + if (ctx->password) { + new_password = kstrdup(ctx->password, GFP_KERNEL); +- if (!new_password) +- return -ENOMEM; +- } else ++ if (!new_password) { ++ rc = -ENOMEM; ++ goto restore_ctx; ++ } ++ } else { + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); ++ } + } + + /* +@@ -1119,11 +1147,29 @@ static int smb3_reconfigure(struct fs_context *fc) + if (ctx->password2) { + new_password2 = kstrdup(ctx->password2, GFP_KERNEL); + if (!new_password2) { +- kfree_sensitive(new_password); +- return -ENOMEM; ++ rc = -ENOMEM; ++ goto restore_ctx; + } +- } else ++ } else { + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2); ++ } ++ ++ /* if rsize or wsize not passed in on remount, use previous values */ ++ ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize; ++ ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize; ++ ++ new_ctx = kzalloc_obj(*new_ctx); ++ if (!new_ctx) { ++ rc = -ENOMEM; ++ goto restore_ctx; ++ } ++ ++ rc = smb3_fs_context_dup(new_ctx, ctx); ++ if (rc) ++ goto restore_ctx; ++ ++ need_mchan_update = ctx->multichannel != cifs_sb->ctx->multichannel || ++ ctx->max_channels != cifs_sb->ctx->max_channels; + + /* + * we may update the passwords in the ses struct below. Make sure we do +@@ -1134,54 +1180,55 @@ static int smb3_reconfigure(struct fs_context *fc) + /* + * smb2_reconnect may swap password and password2 in case session setup + * failed. First get ctx passwords in sync with ses passwords. It should +- * be okay to do this even if this function were to return an error at a +- * later stage ++ * be done before committing new passwords. + */ + rc = smb3_sync_session_ctx_passwords(cifs_sb, ses); + if (rc) { + mutex_unlock(&ses->session_mutex); +- kfree_sensitive(new_password); +- kfree_sensitive(new_password2); +- return rc; ++ goto cleanup_new_ctx; ++ } ++ ++ /* ++ * If multichannel or max_channels has changed, update the session's channels accordingly. ++ * This may add or remove channels to match the new configuration. ++ */ ++ if (need_mchan_update) { ++ /* Prevent concurrent scaling operations */ ++ spin_lock(&ses->ses_lock); ++ if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { ++ spin_unlock(&ses->ses_lock); ++ mutex_unlock(&ses->session_mutex); ++ rc = -EINVAL; ++ goto cleanup_new_ctx; ++ } ++ ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; ++ spin_unlock(&ses->ses_lock); + } + + /* +- * now that allocations for passwords are done, commit them ++ * Commit session passwords before any channel work so newly added ++ * channels authenticate with the new credentials. + */ + if (new_password) { + kfree_sensitive(ses->password); + ses->password = new_password; ++ new_password = NULL; + } + if (new_password2) { + kfree_sensitive(ses->password2); + ses->password2 = new_password2; ++ new_password2 = NULL; + } + +- /* +- * If multichannel or max_channels has changed, update the session's channels accordingly. +- * This may add or remove channels to match the new configuration. +- */ +- if ((ctx->multichannel != cifs_sb->ctx->multichannel) || +- (ctx->max_channels != cifs_sb->ctx->max_channels)) { +- ++ if (need_mchan_update) { + /* Synchronize ses->chan_max with the new mount context */ + smb3_sync_ses_chan_max(ses, ctx->max_channels); +- /* Now update the session's channels to match the new configuration */ +- /* Prevent concurrent scaling operations */ +- spin_lock(&ses->ses_lock); +- if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { +- spin_unlock(&ses->ses_lock); +- mutex_unlock(&ses->session_mutex); +- return -EINVAL; +- } +- ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; +- spin_unlock(&ses->ses_lock); + + mutex_unlock(&ses->session_mutex); + +- rc = smb3_update_ses_channels(ses, ses->server, +- false /* from_reconnect */, +- false /* disable_mchan */); ++ smb3_update_ses_channels(ses, ses->server, ++ false /* from_reconnect */, ++ false /* disable_mchan */); + + /* Clear scaling flag after operation */ + spin_lock(&ses->ses_lock); +@@ -1191,22 +1238,30 @@ static int smb3_reconfigure(struct fs_context *fc) + mutex_unlock(&ses->session_mutex); + } + +- STEAL_STRING(cifs_sb, ctx, domainname); +- STEAL_STRING(cifs_sb, ctx, nodename); +- STEAL_STRING(cifs_sb, ctx, iocharset); +- +- /* if rsize or wsize not passed in on remount, use previous values */ +- ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize; +- ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize; +- + smb3_cleanup_fs_context_contents(cifs_sb->ctx); +- rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); ++ memcpy(cifs_sb->ctx, new_ctx, sizeof(*new_ctx)); ++ kfree(new_ctx); ++ new_ctx = NULL; ++ smb3_cleanup_fs_context(old_ctx); ++ old_ctx = NULL; + smb3_update_mnt_flags(cifs_sb); + #ifdef CONFIG_CIFS_DFS_UPCALL + if (!rc) + rc = dfs_cache_remount_fs(cifs_sb); + #endif + ++ return rc; ++ ++cleanup_new_ctx: ++ smb3_cleanup_fs_context_contents(new_ctx); ++restore_ctx: ++ kfree(new_ctx); ++ kfree_sensitive(new_password); ++ kfree_sensitive(new_password2); ++ smb3_cleanup_fs_context_contents(cifs_sb->ctx); ++ memcpy(cifs_sb->ctx, old_ctx, sizeof(*old_ctx)); ++ kfree(old_ctx); ++ + return rc; + } + +-- +2.53.0 + diff --git a/queue-7.0/cifs-fix-undefined-variables.patch b/queue-7.0/cifs-fix-undefined-variables.patch new file mode 100644 index 0000000000..055fb90f4c --- /dev/null +++ b/queue-7.0/cifs-fix-undefined-variables.patch @@ -0,0 +1,64 @@ +From 8afb5ac1689a2ebca77135c777f2777edf869321 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 22:13:09 +0100 +Subject: cifs: Fix undefined variables + +From: David Howells + +[ Upstream commit 8cf8b5ae8e093132b0dce0a932af10c9ef077936 ] + +Fix a couple of undefined variables introduced by the patch to fix tearing +on ->remote_i_size and ->zero_point. For some reason, make W=1 with gcc +doesn't give undefined variable warnings (but clang does). + +Fixes: 2c8f4742bb76 ("netfs: Fix potential for tearing in ->remote_i_size and ->zero_point") +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202605031459.eX5UbO3K-lkp@intel.com/ +Closes: https://lore.kernel.org/oe-kbuild-all/202605021450.ca5QGqLH-lkp@intel.com/ +cc: Steve French +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: Christian Brauner +cc: linux-cifs@vger.kernel.org +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Linus Torvalds +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifsfs.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index db6062dcbb3ec..386b0d43f064f 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -1339,7 +1339,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + struct cifsFileInfo *smb_file_src = src_file->private_data; + struct cifsFileInfo *smb_file_target = dst_file->private_data; + struct cifs_tcon *target_tcon, *src_tcon; +- unsigned long long i_size, old_size, new_size, zero_point; ++ unsigned long long i_size, new_size; + unsigned long long destend, fstart, fend; + unsigned int xid; + int rc; +@@ -1407,7 +1407,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + goto unlock; + + spin_lock(&target_inode->i_lock); +- if (fend > zero_point) ++ if (fend > target_cifsi->netfs._zero_point) + netfs_write_zero_point(target_inode, fend + 1); + i_size = target_inode->i_size; + spin_unlock(&target_inode->i_lock); +@@ -1422,7 +1422,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + if (target_tcon->ses->server->ops->duplicate_extents) { + rc = target_tcon->ses->server->ops->duplicate_extents(xid, + smb_file_src, smb_file_target, off, len, destoff); +- if (rc == 0 && new_size > old_size) { ++ if (rc == 0 && new_size > i_size) { + truncate_setsize(target_inode, new_size); + fscache_resize_cookie(cifs_inode_cookie(target_inode), + new_size); +-- +2.53.0 + diff --git a/queue-7.0/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch b/queue-7.0/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch new file mode 100644 index 0000000000..2b64de9e10 --- /dev/null +++ b/queue-7.0/crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch @@ -0,0 +1,214 @@ +From c682aecfd60bf77a711d50018de602e91059253a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 00:05:13 +0100 +Subject: crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks + +From: David Howells + +[ Upstream commit 2b50aceafe6606ea52ed42aadd1b4d44a188aade ] + +Change the krb5 crypto library to provide facilities to precheck the length +of the message about to be decrypted or verified. + +Fix AF_RXRPC to make use of this to validate DATA packets secured with +RxGK. + +Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") +Closes: https://sashiko.dev/#/patchset/20260511160753.607296-1-dhowells%40redhat.com +Signed-off-by: David Howells +cc: Herbert Xu +cc: Simon Horman +cc: Chuck Lever +cc: linux-afs@lists.infradead.org +Reviewed-by: Jeffrey Altman +Tested-by: Marc Dionne +Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + Documentation/crypto/krb5.rst | 17 ++++++++--- + crypto/krb5/krb5_api.c | 54 +++++++++++++++++++++++++++++++---- + include/crypto/krb5.h | 9 ++++-- + include/trace/events/rxrpc.h | 1 + + net/rxrpc/rxgk.c | 15 ++++++++-- + 5 files changed, 81 insertions(+), 15 deletions(-) + +diff --git a/Documentation/crypto/krb5.rst b/Documentation/crypto/krb5.rst +index beffa0133446d..f62e07ac68114 100644 +--- a/Documentation/crypto/krb5.rst ++++ b/Documentation/crypto/krb5.rst +@@ -158,13 +158,22 @@ returned. + When a message has been received, the location and size of the data with the + message can be determined by calling:: + +- void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len); ++ int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len); + + The caller provides the offset and length of the message to the function, which + then alters those values to indicate the region containing the data (plus any +-padding). It is up to the caller to determine how much padding there is. ++padding). It is up to the caller to determine how much padding there is. The ++function returns an error if the length is too small or if the mode is ++unsupported. An additional function:: ++ ++ int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content); ++ ++is provided to just do a basic check that the decrypted/verified message would ++have a sufficient minimum payload. + + Preparation Functions + --------------------- +diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c +index 23026d4206c82..c7ea40f900a77 100644 +--- a/crypto/krb5/krb5_api.c ++++ b/crypto/krb5/krb5_api.c +@@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data); + * Find the offset and size of the data in a secure message so that this + * information can be used in the metadata buffer which will get added to the + * digest by crypto_krb5_verify_mic(). ++ * ++ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if ++ * the mode is unsupported. + */ +-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len) ++int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len) + { + switch (mode) { + case KRB5_CHECKSUM_MODE: ++ if (*_len < krb5->cksum_len) ++ return -EBADMSG; + *_offset += krb5->cksum_len; + *_len -= krb5->cksum_len; +- return; ++ return 0; + case KRB5_ENCRYPT_MODE: ++ if (*_len < krb5->conf_len + krb5->cksum_len) ++ return -EBADMSG; + *_offset += krb5->conf_len; + *_len -= krb5->conf_len + krb5->cksum_len; +- return; ++ return 0; + default: + WARN_ON_ONCE(1); +- return; ++ return -EINVAL; + } + } + EXPORT_SYMBOL(crypto_krb5_where_is_the_data); + ++/** ++ * crypto_krb5_check_data_len - Check a message is big enough ++ * @krb5: The encoding to use. ++ * @mode: Mode of operation. ++ * @len: The length of the secure blob. ++ * @min_content: Minimum length of the content inside the blob. ++ * ++ * Check that a message is large enough to hold whatever bits the encryption ++ * type wants to glue on (nonce, checksum) plus a minimum amount of content. ++ * ++ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if ++ * the mode is unsupported. ++ */ ++int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content) ++{ ++ switch (mode) { ++ case KRB5_CHECKSUM_MODE: ++ if (len < krb5->cksum_len || ++ len - krb5->cksum_len < min_content) ++ return -EBADMSG; ++ return 0; ++ case KRB5_ENCRYPT_MODE: ++ if (len < krb5->conf_len + krb5->cksum_len || ++ len - (krb5->conf_len + krb5->cksum_len) < min_content) ++ return -EBADMSG; ++ return 0; ++ default: ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL(crypto_krb5_check_data_len); ++ + /* + * Prepare the encryption with derived key data. + */ +diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h +index 71dd38f59be1d..aac3ecf88467c 100644 +--- a/include/crypto/krb5.h ++++ b/include/crypto/krb5.h +@@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5, + size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_buffer_size, size_t *_offset); +-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, +- enum krb5_crypto_mode mode, +- size_t *_offset, size_t *_len); ++int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t *_offset, size_t *_len); ++int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, ++ enum krb5_crypto_mode mode, ++ size_t len, size_t min_content); + struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5, + const struct krb5_buffer *TK, + u32 usage, gfp_t gfp); +diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h +index 573f2df3a2c99..704a10de66700 100644 +--- a/include/trace/events/rxrpc.h ++++ b/include/trace/events/rxrpc.h +@@ -71,6 +71,7 @@ + EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \ + EM(rxkad_abort_resp_version, "rxkad-resp-version") \ + /* RxGK security errors */ \ ++ EM(rxgk_abort_1_short_header, "rxgk1-short-hdr") \ + EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \ + EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \ + EM(rxgk_abort_2_short_data, "rxgk2-short-data") \ +diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c +index 0d5e654da918f..26e723052a37e 100644 +--- a/net/rxrpc/rxgk.c ++++ b/net/rxrpc/rxgk.c +@@ -480,8 +480,12 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + + _enter(""); + +- crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, +- &data_offset, &data_len); ++ if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, ++ &data_offset, &data_len) < 0) { ++ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, ++ rxgk_abort_1_short_header); ++ goto put_gk; ++ } + + hdr = kzalloc_obj(*hdr, GFP_NOFS); + if (!hdr) +@@ -529,6 +533,13 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + + _enter(""); + ++ if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE, ++ len, sizeof(hdr)) < 0) { ++ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, ++ rxgk_abort_2_short_header); ++ goto error; ++ } ++ + ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); + if (ret < 0) { + if (ret != -ENOMEM) +-- +2.53.0 + diff --git a/queue-7.0/dma-mapping-move-dma_map_resource-sanity-check-into-.patch b/queue-7.0/dma-mapping-move-dma_map_resource-sanity-check-into-.patch new file mode 100644 index 0000000000..879b149d8c --- /dev/null +++ b/queue-7.0/dma-mapping-move-dma_map_resource-sanity-check-into-.patch @@ -0,0 +1,86 @@ +From 14e01e8c281cbcd6be8263b91de8c39ce1f2c979 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 15:22:09 +0800 +Subject: dma-mapping: move dma_map_resource() sanity check into debug code + +From: Jianpeng Chang + +[ Upstream commit af0c3f05866237f7592219bfe05387bc3bfc99b5 ] + +dma_map_resource() uses pfn_valid() to ensure the range is not RAM. +However, pfn_valid() only checks for availability of the memory map for +a PFN but it does not ensure that the PFN is actually backed by RAM. On +ARM64 with SPARSEMEM (128MB section granularity), MMIO addresses that +share a section with RAM will falsely trigger the WARN_ON_ONCE and cause +dma_map_resource() to return DMA_MAPPING_ERROR. + +This causes a WARNING on Raspberry Pi 4 during spi_bcm2835 probe because +the SPI FIFO register (0xfe204004) falls in the same sparsemem section +as the end of RAM (0xf8000000-0xfbffffff), both in section 31 +(0xf8000000-0xffffffff). + +Move the sanity check from dma_map_resource() into debug_dma_map_phys() +and replace the unreliable pfn_valid() with pfn_valid() && +!PageReserved(), which correctly identifies actual usable RAM without +false positives for MMIO regions that happen to have struct pages. + +Since dma_map_resource() is dma_map_phys(DMA_ATTR_MMIO), the check +applies equally to both APIs. Any non-reserved page represents kernel +memory to a sufficient degree that using DMA_ATTR_MMIO on it is almost +certainly wrong and risks breaking coherency on non-coherent platforms. +ZONE_DEVICE pages used for PCI P2P DMA (MEMORY_DEVICE_PCI_P2PDMA) have +PageReserved set, so they will not trigger a false positive. + +The check no longer blocks the mapping and uses err_printk() to +integrate with dma-debug filtering. + +Fixes: f7326196a781 ("dma-mapping: export new dma_*map_phys() interface") +Reviewed-by: Robin Murphy +Signed-off-by: Jianpeng Chang +Reviewed-by: Leon Romanovsky +Signed-off-by: Marek Szyprowski +Link: https://lore.kernel.org/r/20260513072209.1486986-1-jianpeng.chang.cn@windriver.com +Signed-off-by: Sasha Levin +--- + kernel/dma/debug.c | 9 ++++++++- + kernel/dma/mapping.c | 4 ---- + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c +index 1a725edbbbf6a..3248f8b4d096d 100644 +--- a/kernel/dma/debug.c ++++ b/kernel/dma/debug.c +@@ -1251,7 +1251,14 @@ void debug_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, + entry->direction = direction; + entry->map_err_type = MAP_ERR_NOT_CHECKED; + +- if (!(attrs & DMA_ATTR_MMIO)) { ++ if (attrs & DMA_ATTR_MMIO) { ++ unsigned long pfn = PHYS_PFN(phys); ++ ++ if (pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn))) ++ err_printk(dev, entry, ++ "dma_map_resource called for RAM address %pa\n", ++ &phys); ++ } else { + check_for_stack(dev, phys); + + if (!PhysHighMem(phys)) +diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c +index 6d3dd0bd3a886..5d59372f42770 100644 +--- a/kernel/dma/mapping.c ++++ b/kernel/dma/mapping.c +@@ -356,10 +356,6 @@ EXPORT_SYMBOL(dma_unmap_sg_attrs); + dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, + size_t size, enum dma_data_direction dir, unsigned long attrs) + { +- if (IS_ENABLED(CONFIG_DMA_API_DEBUG) && +- WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr)))) +- return DMA_MAPPING_ERROR; +- + return dma_map_phys(dev, phys_addr, size, dir, attrs | DMA_ATTR_MMIO); + } + EXPORT_SYMBOL(dma_map_resource); +-- +2.53.0 + diff --git a/queue-7.0/documentation-intel_pstate-fix-description-of-asymme.patch b/queue-7.0/documentation-intel_pstate-fix-description-of-asymme.patch new file mode 100644 index 0000000000..6d133a083b --- /dev/null +++ b/queue-7.0/documentation-intel_pstate-fix-description-of-asymme.patch @@ -0,0 +1,60 @@ +From 4c5887d0bf3976b6b973985a22418de4cc622096 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 14:41:13 -0700 +Subject: Documentation: intel_pstate: Fix description of asymmetric packing + with SMT + +From: Ricardo Neri + +[ Upstream commit ee047fc7a2da90554410128195058c409a391d43 ] + +Patchset [1], including commits + + 046a5a95c3b0 ("x86/sched/itmt: Give all SMT siblings of a core the same priority") + 995998ebdebd ("x86/sched: Remove SD_ASYM_PACKING from the SMT domain flags") + +overhauled asym_packing handling in the scheduler on x86 hybrid +processors with SMT. It removed SD_ASYM_PACKING from the x86 SMT +scheduling domain and made all SMT siblings of a core share the same +priority. As a result, asym_packing operates only across physical +cores, spreading tasks among them and only using idle SMT siblings +once all physical cores are busy. + +Fix the documentation to reflect this behavior. + +Fixes: f20af84c29b2 ("cpufreq: intel_pstate: Document hybrid processor support") +Link: https://lore.kernel.org/r/20230406203148.19182-1-ricardo.neri-calderon@linux.intel.com [1] +Signed-off-by: Ricardo Neri +[ rjw: Changelog edits ] +Link: https://patch.msgid.link/20260424-rneri-fix-intel-pstate-doc-smt-asym-packing-v1-1-317bf7d5c362@linux.intel.com +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +--- + Documentation/admin-guide/pm/intel_pstate.rst | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst +index fde967b0c2e0e..25fe5d88fea6c 100644 +--- a/Documentation/admin-guide/pm/intel_pstate.rst ++++ b/Documentation/admin-guide/pm/intel_pstate.rst +@@ -355,11 +355,12 @@ HyperThreading (HT) in the context of Intel processors, is enabled on at least + one core, ``intel_pstate`` assigns performance-based priorities to CPUs. Namely, + the priority of a given CPU reflects its highest HWP performance level which + causes the CPU scheduler to generally prefer more performant CPUs, so the less +-performant CPUs are used when the other ones are fully loaded. However, SMT +-siblings (that is, logical CPUs sharing one physical core) are treated in a +-special way such that if one of them is in use, the effective priority of the +-other ones is lowered below the priorities of the CPUs located in the other +-physical cores. ++performant CPUs are used when the other ones are fully loaded. SMT siblings ++(that is, logical CPUs sharing one physical core) are given the same priority. ++The scheduler can pull tasks from lower-priority cores and place them on any ++sibling. Since the scheduler spreads tasks among physical cores, tasks will be ++placed on the SMT siblings of physical cores only after all physical cores are ++busy. + + This approach maximizes performance in the majority of cases, but unfortunately + it also leads to excessive energy usage in some important scenarios, like video +-- +2.53.0 + diff --git a/queue-7.0/documentation-laptops-update-documentation-for-uniwi.patch b/queue-7.0/documentation-laptops-update-documentation-for-uniwi.patch new file mode 100644 index 0000000000..ca118a6b81 --- /dev/null +++ b/queue-7.0/documentation-laptops-update-documentation-for-uniwi.patch @@ -0,0 +1,93 @@ +From cff53153f5a6ac5f6e22a0fde5cbc8c55c86dcb8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Mar 2026 21:32:12 +0100 +Subject: Documentation: laptops: Update documentation for uniwill laptops +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Werner Sembach + +[ Upstream commit 9ec6bf62cf98e30c7126a0f51ee7cdf2e8d458b6 ] + +Adds short description for two new sysfs entries, ctgp_offset and +usb_c_power_priority, to the documentation of uniwill laptops. + +Reviewed-by: Armin Wolf +Reviewed-by: Shuah Khan +Signed-off-by: Werner Sembach +Link: https://patch.msgid.link/20260324203413.454361-6-wse@tuxedocomputers.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Stable-dep-of: 26cbe119f99c ("platform/x86: uniwill-laptop: Do not enable the charging limit even when forced") +Signed-off-by: Sasha Levin +--- + .../ABI/testing/sysfs-driver-uniwill-laptop | 27 +++++++++++++++++++ + .../admin-guide/laptops/uniwill-laptop.rst | 12 +++++++++ + 2 files changed, 39 insertions(+) + +diff --git a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop +index 2df70792968f3..2397c65c969a6 100644 +--- a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop ++++ b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop +@@ -51,3 +51,30 @@ Description: + + Reading this file returns the current status of the breathing animation + functionality. ++ ++What: /sys/bus/platform/devices/INOU0000:XX/ctgp_offset ++Date: January 2026 ++KernelVersion: 7.0 ++Contact: Werner Sembach ++Description: ++ Allows userspace applications to set the configurable TGP offset on top of the base ++ TGP. Base TGP and max TGP and therefore the max cTGP offset are device specific. ++ Note that setting the maximum cTGP leaves no window open for Dynamic Boost as ++ Dynamic Boost also can not go over max TGP. Setting the cTGP to maximum is ++ effectively disabling Dynamic Boost and telling the device to always prioritize the ++ GPU over the CPU. ++ ++ Reading this file returns the current configurable TGP offset. ++ ++What: /sys/bus/platform/devices/INOU0000:XX/usb_c_power_priority ++Date: February 2026 ++KernelVersion: 7.1 ++Contact: Werner Sembach ++Description: ++ Allows userspace applications to choose the USB-C power distribution profile between ++ one that offers a bigger share of the power to the battery and one that offers more ++ of it to the CPU. Writing "charging"/"performance" into this file selects the ++ respective profile. ++ ++ Reading this file returns the profile names with the currently active one in ++ brackets. +diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst +index aff5f57a6bd47..561334865feb7 100644 +--- a/Documentation/admin-guide/laptops/uniwill-laptop.rst ++++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst +@@ -50,6 +50,10 @@ between 1 and 100 percent are supported. + Additionally the driver signals the presence of battery charging issues through the standard + ``health`` power supply sysfs attribute. + ++It also lets you set whether a USB-C power source should prioritise charging the battery or ++delivering immediate power to the cpu. See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for ++details. ++ + Lightbar + -------- + +@@ -58,3 +62,11 @@ LED class device. The default name of this LED class device is ``uniwill:multico + + See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details on how to control the various + animation modes of the lightbar. ++ ++Configurable TGP ++---------------- ++ ++The ``uniwill-laptop`` driver allows to set the configurable TGP for devices with NVIDIA GPUs that ++allow it. ++ ++See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. +-- +2.53.0 + diff --git a/queue-7.0/drm-amdgpu-align-amdgpu_gtt_mgr-entries-to-tlb-size-.patch b/queue-7.0/drm-amdgpu-align-amdgpu_gtt_mgr-entries-to-tlb-size-.patch new file mode 100644 index 0000000000..bd51500272 --- /dev/null +++ b/queue-7.0/drm-amdgpu-align-amdgpu_gtt_mgr-entries-to-tlb-size-.patch @@ -0,0 +1,59 @@ +From 489a88a40be0233795d6be6472d26951b2bd7176 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 22:04:08 +0200 +Subject: drm/amdgpu: Align amdgpu_gtt_mgr entries to TLB size on Tahiti (v2) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Timur Kristóf + +[ Upstream commit 4d798ea0712fddbd35b439cef32b8ac735eb76f9 ] + +The TLB is organized in groups of 8 entries, each one is 4K. +On Tahiti, the HW requires these GART entries to be 32K-aligned. + +This fixes a VCE 1 firmware validation failure that can happen +after suspend/resume since we use amdgpu_gtt_mgr for VCE 1. + +v2: +- Change variable declaration order +- Add comment about "V bit HW bug" + +Fixes: 698fa62f56aa ("drm/amdgpu: Add helper to alloc GART entries") +Signed-off-by: Timur Kristóf +Reviewed-by: Christian König +Signed-off-by: Alex Deucher +(cherry picked from commit 530411b465ef0b2c0cc18c2e3d7e38422b1117d1) +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +index ac276bb53c7c2..9dd6cfd6c0fe0 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +@@ -199,11 +199,18 @@ int amdgpu_gtt_mgr_alloc_entries(struct amdgpu_gtt_mgr *mgr, + enum drm_mm_insert_mode mode) + { + struct amdgpu_device *adev = container_of(mgr, typeof(*adev), mman.gtt_mgr); ++ u32 alignment = 0; + int r; + ++ /* Align to TLB L2 cache entry size to work around "V bit HW bug" */ ++ if (adev->asic_type == CHIP_TAHITI) { ++ alignment = 32 * 1024 / AMDGPU_GPU_PAGE_SIZE; ++ num_pages = ALIGN(num_pages, alignment); ++ } ++ + spin_lock(&mgr->lock); + r = drm_mm_insert_node_in_range(&mgr->mm, mm_node, num_pages, +- 0, GART_ENTRY_WITHOUT_BO_COLOR, 0, ++ alignment, GART_ENTRY_WITHOUT_BO_COLOR, 0, + adev->gmc.gart_size >> PAGE_SHIFT, + mode); + spin_unlock(&mgr->lock); +-- +2.53.0 + diff --git a/queue-7.0/drm-amdgpu-vce1-check-that-the-gpu-address-is-128-mi.patch b/queue-7.0/drm-amdgpu-vce1-check-that-the-gpu-address-is-128-mi.patch new file mode 100644 index 0000000000..206142d732 --- /dev/null +++ b/queue-7.0/drm-amdgpu-vce1-check-that-the-gpu-address-is-128-mi.patch @@ -0,0 +1,80 @@ +From d9b8390a2930b5cbf0c6b031e527247417868e8f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 22:04:09 +0200 +Subject: drm/amdgpu/vce1: Check that the GPU address is < 128 MiB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Timur Kristóf + +[ Upstream commit 9f907adb66d8369dd45412794a04845011503fa8 ] + +When ensuring the low 32-bit address, make sure it is +less than 128 MiB, otherwise the VCE seems to fail to initialize. +This seems to be an undocumented limitation of the firmware +validation mechanism. Note that in case of VCE1 the BAR +address is zero and we can't change it also due to the +firmware validator. + +When programming the mmVCE_VCPU_CACHE_OFFSETn registers, +don't AND them with a mask. This is incorrect because +the register mask is actually 0x0fffffff and useless because +we already ensure the addresses are below the limit. + +Signed-off-by: Timur Kristóf +Reviewed-by: Christian König +Signed-off-by: Alex Deucher +(cherry picked from commit e729ae5f3ac73c861c062080ac8c3d666c972404) +Stable-dep-of: 3e5a1d5bb2ff ("drm/amdgpu/vce1: Fix VCE 1 firmware size and offsets") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/vce_v1_0.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c +index 9ae4246185560..0b69773b71848 100644 +--- a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c +@@ -318,17 +318,17 @@ static int vce_v1_0_mc_resume(struct amdgpu_device *adev) + + offset = adev->vce.gpu_addr + AMDGPU_VCE_FIRMWARE_OFFSET; + size = VCE_V1_0_FW_SIZE; +- WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff); ++ WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE0, size); + + offset += size; + size = VCE_V1_0_STACK_SIZE; +- WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0x7fffffff); ++ WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE1, size); + + offset += size; + size = VCE_V1_0_DATA_SIZE; +- WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0x7fffffff); ++ WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE2, size); + + WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100); +@@ -532,12 +532,16 @@ static int vce_v1_0_early_init(struct amdgpu_ip_block *ip_block) + * To accomodate that, we put GART to the LOW address range + * and reserve some GART pages where we map the VCPU BO, + * so that it gets a 32-bit address. ++ * ++ * The BAR address is zero and we can't change it ++ * due to the firmware validation mechanism. ++ * It seems that it fails to initialize if the address is >= 128 MiB. + */ + static int vce_v1_0_ensure_vcpu_bo_32bit_addr(struct amdgpu_device *adev) + { + u64 gpu_addr = amdgpu_bo_gpu_offset(adev->vce.vcpu_bo); + u64 bo_size = amdgpu_bo_size(adev->vce.vcpu_bo); +- u64 max_vcpu_bo_addr = 0xffffffff - bo_size; ++ u64 max_vcpu_bo_addr = 0x07ffffff - bo_size; + u64 num_pages = ALIGN(bo_size, AMDGPU_GPU_PAGE_SIZE) / AMDGPU_GPU_PAGE_SIZE; + u64 pa = amdgpu_gmc_vram_pa(adev, adev->vce.vcpu_bo); + u64 flags = AMDGPU_PTE_READABLE | AMDGPU_PTE_WRITEABLE | AMDGPU_PTE_VALID; +-- +2.53.0 + diff --git a/queue-7.0/drm-amdgpu-vce1-fix-vce-1-firmware-size-and-offsets.patch b/queue-7.0/drm-amdgpu-vce1-fix-vce-1-firmware-size-and-offsets.patch new file mode 100644 index 0000000000..fb63c86d6b --- /dev/null +++ b/queue-7.0/drm-amdgpu-vce1-fix-vce-1-firmware-size-and-offsets.patch @@ -0,0 +1,109 @@ +From abd256366b33524e467917e12d9cd6b82f814057 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 22:04:13 +0200 +Subject: drm/amdgpu/vce1: Fix VCE 1 firmware size and offsets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Timur Kristóf + +[ Upstream commit 3e5a1d5bb2ff061e64c7992f8e5404dfd4c2d0f3 ] + +The VCPU BO contains the actual FW at an offset, but +it was not calculated into the VCPU BO size. +Subtract this from the FW size to make sure there is +no out of bounds access. + +Make sure the stack and data offsets are aligned to +the 32K TLB size. + +Check that the FW microcode actually fits in the +space that is reserved for it. + +Fixes: d4a640d4b9f3 ("drm/amdgpu/vce1: Implement VCE1 IP block (v2)") +Signed-off-by: Timur Kristóf +Reviewed-by: Christian König +Signed-off-by: Alex Deucher +(cherry picked from commit c16fe59f622a080fc457a57b3e8f14c780699449) +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/vce_v1_0.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c +index 0b69773b71848..d63ff64943d58 100644 +--- a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c +@@ -42,9 +42,10 @@ + #include "oss/oss_1_0_d.h" + #include "oss/oss_1_0_sh_mask.h" + ++#define VCE_V1_0_ALIGNMENT (32 * 1024) + #define VCE_V1_0_FW_SIZE (256 * 1024) + #define VCE_V1_0_STACK_SIZE (64 * 1024) +-#define VCE_V1_0_DATA_SIZE (7808 * (AMDGPU_MAX_VCE_HANDLES + 1)) ++#define VCE_V1_0_DATA_SIZE (ALIGN(7808 * (AMDGPU_MAX_VCE_HANDLES + 1), VCE_V1_0_ALIGNMENT)) + #define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK 0x02 + + #define VCE_V1_0_GART_PAGE_START \ +@@ -194,17 +195,22 @@ static int vce_v1_0_load_fw_signature(struct amdgpu_device *adev) + { + const struct common_firmware_header *hdr; + struct vce_v1_0_fw_signature *sign; +- unsigned int ucode_offset; ++ u32 ucode_offset; ++ u32 ucode_size; + uint32_t chip_id; + u32 *cpu_addr; + int i; + + hdr = (const struct common_firmware_header *)adev->vce.fw->data; + ucode_offset = le32_to_cpu(hdr->ucode_array_offset_bytes); ++ ucode_size = hdr->ucode_size_bytes - sizeof(struct vce_v1_0_fw_signature *); + cpu_addr = adev->vce.cpu_addr; + + sign = (void *)adev->vce.fw->data + ucode_offset; + ++ if (ucode_size > VCE_V1_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET) ++ return -EINVAL; ++ + switch (adev->asic_type) { + case CHIP_TAHITI: + chip_id = 0x01000014; +@@ -236,7 +242,7 @@ static int vce_v1_0_load_fw_signature(struct amdgpu_device *adev) + cpu_addr[4] = cpu_to_le32(le32_to_cpu(sign->length) + 64); + + memset_io(&cpu_addr[5], 0, 44); +- memcpy_toio(&cpu_addr[16], &sign[1], hdr->ucode_size_bytes - sizeof(*sign)); ++ memcpy_toio(&cpu_addr[16], &sign[1], ucode_size); + + cpu_addr += (le32_to_cpu(sign->length) + 64) / 4; + memcpy_toio(&cpu_addr[0], &sign->val[i].sigval[0], 16); +@@ -317,17 +323,22 @@ static int vce_v1_0_mc_resume(struct amdgpu_device *adev) + WREG32(mmVCE_VCPU_SCRATCH7, AMDGPU_MAX_VCE_HANDLES); + + offset = adev->vce.gpu_addr + AMDGPU_VCE_FIRMWARE_OFFSET; +- size = VCE_V1_0_FW_SIZE; ++ size = VCE_V1_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET; + WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE0, size); + + offset += size; + size = VCE_V1_0_STACK_SIZE; ++ WARN_ON(!IS_ALIGNED(offset, VCE_V1_0_ALIGNMENT)); ++ WARN_ON(!IS_ALIGNED(size, VCE_V1_0_ALIGNMENT)); + WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE1, size); + + offset += size; + size = VCE_V1_0_DATA_SIZE; ++ WARN_ON(!IS_ALIGNED(offset, VCE_V1_0_ALIGNMENT)); ++ WARN_ON(!IS_ALIGNED(size, VCE_V1_0_ALIGNMENT)); ++ WARN_ON((offset + size - adev->vce.gpu_addr) > amdgpu_bo_size(adev->vce.vcpu_bo)); + WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset); + WREG32(mmVCE_VCPU_CACHE_SIZE2, size); + +-- +2.53.0 + diff --git a/queue-7.0/drm-gem-make-the-gem-lru-lock-part-of-drm_device.patch b/queue-7.0/drm-gem-make-the-gem-lru-lock-part-of-drm_device.patch new file mode 100644 index 0000000000..c24c706df6 --- /dev/null +++ b/queue-7.0/drm-gem-make-the-gem-lru-lock-part-of-drm_device.patch @@ -0,0 +1,548 @@ +From 87e145dfa9fb5652507d7741afb05e0b36296e74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 13:41:45 +0200 +Subject: drm/gem: Make the GEM LRU lock part of drm_device + +From: Boris Brezillon + +[ Upstream commit 379e8f1ca5e919b130b40d8115d92a536e5f8d7a ] + +Recently, a few races have been discovered in the GEM LRU logic, all +of them caused by the fact the LRU lock is accessed through +gem->lru->lock, and that very same lock also protects changes to +gem->lru, leading to situations where gem->lru needs to first be +accessed without the lock held, to then get the lru to access the lock +through and finally take the lock and do the expected operation. + +Currently, the only driver making use of this API (MSM) declares a +device-wide lock, and the user we're about to add (panthor) will +do the same. There's no evidence that we will ever have a driver +that wants different pools of LRUs protected by different locks under +the same drm_device. So we're better off moving this lock to drm_device +and always locking it through obj->dev->gem_lru_mutex, or directly +through dev->gem_lru_mutex. + +If anyone ever needs more fine-grained locking, this can be revisited +to pass some drm_gem_lru_pool object representing the pool of LRUs +under a specific lock, but for now, the per-device lock seems to be +enough. + +Fixes: e7c2af13f811 ("drm/gem: Add LRU/shrinker helper") +Reported-by: Chia-I Wu +Closes: https://gitlab.freedesktop.org/panfrost/linux/-/work_items/86 +Reviewed-by: Rob Clark +Reviewed-by: Liviu Dudau +Reviewed-by: Steven Price +Reviewed-by: Chia-I Wu +Link: https://patch.msgid.link/20260518-panthor-shrinker-fixes-v4-1-1920234470d5@collabora.com +Signed-off-by: Boris Brezillon +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/drm_drv.c | 2 ++ + drivers/gpu/drm/drm_gem.c | 36 ++++++++++++-------------- + drivers/gpu/drm/msm/msm_drv.c | 11 ++++---- + drivers/gpu/drm/msm/msm_drv.h | 7 ----- + drivers/gpu/drm/msm/msm_gem.c | 33 ++++++++++++----------- + drivers/gpu/drm/msm/msm_gem_shrinker.c | 4 +-- + drivers/gpu/drm/msm/msm_gem_submit.c | 6 ++--- + drivers/gpu/drm/msm/msm_gem_vma.c | 12 ++++----- + drivers/gpu/drm/msm/msm_ringbuffer.c | 6 ++--- + include/drm/drm_device.h | 7 +++++ + include/drm/drm_gem.h | 20 +++++++------- + 11 files changed, 69 insertions(+), 75 deletions(-) + +diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c +index 2915118436ce8..0238445cf96cf 100644 +--- a/drivers/gpu/drm/drm_drv.c ++++ b/drivers/gpu/drm/drm_drv.c +@@ -696,6 +696,7 @@ static void drm_dev_init_release(struct drm_device *dev, void *res) + mutex_destroy(&dev->master_mutex); + mutex_destroy(&dev->clientlist_mutex); + mutex_destroy(&dev->filelist_mutex); ++ mutex_destroy(&dev->gem_lru_mutex); + } + + static int drm_dev_init(struct drm_device *dev, +@@ -737,6 +738,7 @@ static int drm_dev_init(struct drm_device *dev, + INIT_LIST_HEAD(&dev->vblank_event_list); + + spin_lock_init(&dev->event_lock); ++ mutex_init(&dev->gem_lru_mutex); + mutex_init(&dev->filelist_mutex); + mutex_init(&dev->clientlist_mutex); + mutex_init(&dev->master_mutex); +diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c +index 2b152e3103c32..52151452adf98 100644 +--- a/drivers/gpu/drm/drm_gem.c ++++ b/drivers/gpu/drm/drm_gem.c +@@ -1543,12 +1543,10 @@ EXPORT_SYMBOL(drm_gem_unlock_reservations); + * drm_gem_lru_init - initialize a LRU + * + * @lru: The LRU to initialize +- * @lock: The lock protecting the LRU + */ + void +-drm_gem_lru_init(struct drm_gem_lru *lru, struct mutex *lock) ++drm_gem_lru_init(struct drm_gem_lru *lru) + { +- lru->lock = lock; + lru->count = 0; + INIT_LIST_HEAD(&lru->list); + } +@@ -1573,14 +1571,10 @@ drm_gem_lru_remove_locked(struct drm_gem_object *obj) + void + drm_gem_lru_remove(struct drm_gem_object *obj) + { +- struct drm_gem_lru *lru = obj->lru; +- +- if (!lru) +- return; +- +- mutex_lock(lru->lock); +- drm_gem_lru_remove_locked(obj); +- mutex_unlock(lru->lock); ++ mutex_lock(&obj->dev->gem_lru_mutex); ++ if (obj->lru) ++ drm_gem_lru_remove_locked(obj); ++ mutex_unlock(&obj->dev->gem_lru_mutex); + } + EXPORT_SYMBOL(drm_gem_lru_remove); + +@@ -1595,7 +1589,7 @@ EXPORT_SYMBOL(drm_gem_lru_remove); + void + drm_gem_lru_move_tail_locked(struct drm_gem_lru *lru, struct drm_gem_object *obj) + { +- lockdep_assert_held_once(lru->lock); ++ lockdep_assert_held_once(&obj->dev->gem_lru_mutex); + + if (obj->lru) + drm_gem_lru_remove_locked(obj); +@@ -1619,9 +1613,9 @@ EXPORT_SYMBOL(drm_gem_lru_move_tail_locked); + void + drm_gem_lru_move_tail(struct drm_gem_lru *lru, struct drm_gem_object *obj) + { +- mutex_lock(lru->lock); ++ mutex_lock(&obj->dev->gem_lru_mutex); + drm_gem_lru_move_tail_locked(lru, obj); +- mutex_unlock(lru->lock); ++ mutex_unlock(&obj->dev->gem_lru_mutex); + } + EXPORT_SYMBOL(drm_gem_lru_move_tail); + +@@ -1635,6 +1629,7 @@ EXPORT_SYMBOL(drm_gem_lru_move_tail); + * of the shrink callback to check for this (ie. dma_resv_test_signaled()) + * or if necessary block until the buffer becomes idle. + * ++ * @dev: DRM device the LRU belongs to + * @lru: The LRU to scan + * @nr_to_scan: The number of pages to try to reclaim + * @remaining: The number of pages left to reclaim, should be initialized by caller +@@ -1642,7 +1637,8 @@ EXPORT_SYMBOL(drm_gem_lru_move_tail); + * @ticket: Optional ww_acquire_ctx context to use for locking + */ + unsigned long +-drm_gem_lru_scan(struct drm_gem_lru *lru, ++drm_gem_lru_scan(struct drm_device *dev, ++ struct drm_gem_lru *lru, + unsigned int nr_to_scan, + unsigned long *remaining, + bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket), +@@ -1652,9 +1648,9 @@ drm_gem_lru_scan(struct drm_gem_lru *lru, + struct drm_gem_object *obj; + unsigned freed = 0; + +- drm_gem_lru_init(&still_in_lru, lru->lock); ++ drm_gem_lru_init(&still_in_lru); + +- mutex_lock(lru->lock); ++ mutex_lock(&dev->gem_lru_mutex); + + while (freed < nr_to_scan) { + obj = list_first_entry_or_null(&lru->list, typeof(*obj), lru_node); +@@ -1677,7 +1673,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru, + * rest of the loop body, to reduce contention with other + * code paths that need the LRU lock + */ +- mutex_unlock(lru->lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + if (ticket) + ww_acquire_init(ticket, &reservation_ww_class); +@@ -1711,7 +1707,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru, + + tail: + drm_gem_object_put(obj); +- mutex_lock(lru->lock); ++ mutex_lock(&dev->gem_lru_mutex); + } + + /* +@@ -1723,7 +1719,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru, + list_splice_tail(&still_in_lru.list, &lru->list); + lru->count += still_in_lru.count; + +- mutex_unlock(lru->lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + return freed; + } +diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c +index 195f40e331e5a..cc2bcd14b1c26 100644 +--- a/drivers/gpu/drm/msm/msm_drv.c ++++ b/drivers/gpu/drm/msm/msm_drv.c +@@ -128,11 +128,10 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv, + /* + * Initialize the LRUs: + */ +- mutex_init(&priv->lru.lock); +- drm_gem_lru_init(&priv->lru.unbacked, &priv->lru.lock); +- drm_gem_lru_init(&priv->lru.pinned, &priv->lru.lock); +- drm_gem_lru_init(&priv->lru.willneed, &priv->lru.lock); +- drm_gem_lru_init(&priv->lru.dontneed, &priv->lru.lock); ++ drm_gem_lru_init(&priv->lru.unbacked); ++ drm_gem_lru_init(&priv->lru.pinned); ++ drm_gem_lru_init(&priv->lru.willneed); ++ drm_gem_lru_init(&priv->lru.dontneed); + + /* Initialize stall-on-fault */ + spin_lock_init(&priv->fault_stall_lock); +@@ -140,7 +139,7 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv, + + /* Teach lockdep about lock ordering wrt. shrinker: */ + fs_reclaim_acquire(GFP_KERNEL); +- might_lock(&priv->lru.lock); ++ might_lock(&ddev->gem_lru_mutex); + fs_reclaim_release(GFP_KERNEL); + + if (priv->kms_init) { +diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h +index 6d847d593f1ae..617b3c4b42c0c 100644 +--- a/drivers/gpu/drm/msm/msm_drv.h ++++ b/drivers/gpu/drm/msm/msm_drv.h +@@ -150,13 +150,6 @@ struct msm_drm_private { + * DONTNEED state (ie. can be purged) + */ + struct drm_gem_lru dontneed; +- +- /** +- * lock: +- * +- * Protects manipulation of all of the LRUs. +- */ +- struct mutex lock; + } lru; + + struct notifier_block vmap_notifier; +diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c +index 2cb3ab04f1250..efd3d3c9a4490 100644 +--- a/drivers/gpu/drm/msm/msm_gem.c ++++ b/drivers/gpu/drm/msm/msm_gem.c +@@ -177,11 +177,11 @@ static void update_lru_locked(struct drm_gem_object *obj) + + static void update_lru(struct drm_gem_object *obj) + { +- struct msm_drm_private *priv = obj->dev->dev_private; ++ struct drm_device *dev = obj->dev; + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + update_lru_locked(obj); +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + } + + static struct page **get_pages(struct drm_gem_object *obj) +@@ -292,11 +292,11 @@ void msm_gem_pin_obj_locked(struct drm_gem_object *obj) + + static void pin_obj_locked(struct drm_gem_object *obj) + { +- struct msm_drm_private *priv = obj->dev->dev_private; ++ struct drm_device *dev = obj->dev; + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + msm_gem_pin_obj_locked(obj); +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + } + + struct page **msm_gem_pin_pages_locked(struct drm_gem_object *obj) +@@ -487,16 +487,16 @@ int msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct drm_gpuva *vma) + + void msm_gem_unpin_locked(struct drm_gem_object *obj) + { +- struct msm_drm_private *priv = obj->dev->dev_private; ++ struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + msm_gem_assert_locked(obj); + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + msm_obj->pin_count--; + GEM_WARN_ON(msm_obj->pin_count < 0); + update_lru_locked(obj); +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + } + + /* Special unpin path for use in fence-signaling path, avoiding the need +@@ -507,10 +507,10 @@ void msm_gem_unpin_locked(struct drm_gem_object *obj) + */ + void msm_gem_unpin_active(struct drm_gem_object *obj) + { +- struct msm_drm_private *priv = obj->dev->dev_private; ++ struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + +- GEM_WARN_ON(!mutex_is_locked(&priv->lru.lock)); ++ GEM_WARN_ON(!mutex_is_locked(&dev->gem_lru_mutex)); + + msm_obj->pin_count--; + GEM_WARN_ON(msm_obj->pin_count < 0); +@@ -797,12 +797,12 @@ void msm_gem_put_vaddr(struct drm_gem_object *obj) + */ + int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv) + { +- struct msm_drm_private *priv = obj->dev->dev_private; ++ struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + msm_gem_lock(obj); + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + + if (msm_obj->madv != __MSM_MADV_PURGED) + msm_obj->madv = madv; +@@ -814,7 +814,7 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv) + */ + update_lru_locked(obj); + +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + msm_gem_unlock(obj); + +@@ -824,7 +824,6 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv) + void msm_gem_purge(struct drm_gem_object *obj) + { + struct drm_device *dev = obj->dev; +- struct msm_drm_private *priv = obj->dev->dev_private; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + msm_gem_assert_locked(obj); +@@ -839,10 +838,10 @@ void msm_gem_purge(struct drm_gem_object *obj) + + put_pages(obj); + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + /* A one-way transition: */ + msm_obj->madv = __MSM_MADV_PURGED; +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + drm_gem_free_mmap_offset(obj); + +diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c +index 6e39e4e578bba..c8dda2b68cff2 100644 +--- a/drivers/gpu/drm/msm/msm_gem_shrinker.c ++++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c +@@ -178,7 +178,7 @@ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) + * 'ticket' not needed on trylock paths + */ + stages[i].freed = +- drm_gem_lru_scan(stages[i].lru, nr, ++ drm_gem_lru_scan(priv->dev, stages[i].lru, nr, + &stages[i].remaining, + stages[i].shrink, + NULL); +@@ -247,7 +247,7 @@ msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr) + unsigned long remaining = 0; + + for (idx = 0; lrus[idx] && unmapped < vmap_shrink_limit; idx++) { +- unmapped += drm_gem_lru_scan(lrus[idx], ++ unmapped += drm_gem_lru_scan(priv->dev, lrus[idx], + vmap_shrink_limit - unmapped, + &remaining, + vmap_shrink, +diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c +index 75d9f35743700..771d7bb12c2de 100644 +--- a/drivers/gpu/drm/msm/msm_gem_submit.c ++++ b/drivers/gpu/drm/msm/msm_gem_submit.c +@@ -350,7 +350,7 @@ static int submit_fence_sync(struct msm_gem_submit *submit) + + static int submit_pin_objects(struct msm_gem_submit *submit) + { +- struct msm_drm_private *priv = submit->dev->dev_private; ++ struct drm_device *dev = submit->dev; + int i, ret = 0; + + for (i = 0; i < submit->nr_bos; i++) { +@@ -379,11 +379,11 @@ static int submit_pin_objects(struct msm_gem_submit *submit) + * get_pages() which could trigger reclaim.. and if we held the LRU lock + * could trigger deadlock with the shrinker). + */ +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + for (i = 0; i < submit->nr_bos; i++) { + msm_gem_pin_obj_locked(submit->bos[i].obj); + } +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + submit->bos_pinned = true; + +diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c +index 9e3632019bc92..3b418ee32658d 100644 +--- a/drivers/gpu/drm/msm/msm_gem_vma.c ++++ b/drivers/gpu/drm/msm/msm_gem_vma.c +@@ -696,7 +696,7 @@ static struct dma_fence * + msm_vma_job_run(struct drm_sched_job *_job) + { + struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job); +- struct msm_drm_private *priv = job->vm->drm->dev_private; ++ struct drm_device *dev = job->vm->drm; + struct msm_gem_vm *vm = to_msm_vm(job->vm); + struct drm_gem_object *obj; + int ret = vm->unusable ? -EINVAL : 0; +@@ -739,13 +739,13 @@ msm_vma_job_run(struct drm_sched_job *_job) + if (ret) + msm_gem_vm_unusable(job->vm); + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + + job_foreach_bo (obj, job) { + msm_gem_unpin_active(obj); + } + +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + /* VM_BIND ops are synchronous, so no fence to wait on: */ + return NULL; +@@ -1299,7 +1299,7 @@ vm_bind_job_pin_objects(struct msm_vm_bind_job *job) + return PTR_ERR(pages); + } + +- struct msm_drm_private *priv = job->vm->drm->dev_private; ++ struct drm_device *dev = job->vm->drm; + + /* + * A second loop while holding the LRU lock (a) avoids acquiring/dropping +@@ -1308,10 +1308,10 @@ vm_bind_job_pin_objects(struct msm_vm_bind_job *job) + * get_pages() which could trigger reclaim.. and if we held the LRU lock + * could trigger deadlock with the shrinker). + */ +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + job_foreach_bo (obj, job) + msm_gem_pin_obj_locked(obj); +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + job->bos_pinned = true; + +diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c +index 30ddb5351e983..2d6b930b766ec 100644 +--- a/drivers/gpu/drm/msm/msm_ringbuffer.c ++++ b/drivers/gpu/drm/msm/msm_ringbuffer.c +@@ -16,13 +16,13 @@ static struct dma_fence *msm_job_run(struct drm_sched_job *job) + struct msm_gem_submit *submit = to_msm_submit(job); + struct msm_fence_context *fctx = submit->ring->fctx; + struct msm_gpu *gpu = submit->gpu; +- struct msm_drm_private *priv = gpu->dev->dev_private; ++ struct drm_device *dev = gpu->dev; + unsigned nr_cmds = submit->nr_cmds; + int i; + + msm_fence_init(submit->hw_fence, fctx); + +- mutex_lock(&priv->lru.lock); ++ mutex_lock(&dev->gem_lru_mutex); + + for (i = 0; i < submit->nr_bos; i++) { + struct drm_gem_object *obj = submit->bos[i].obj; +@@ -32,7 +32,7 @@ static struct dma_fence *msm_job_run(struct drm_sched_job *job) + + submit->bos_pinned = false; + +- mutex_unlock(&priv->lru.lock); ++ mutex_unlock(&dev->gem_lru_mutex); + + /* TODO move submit path over to using a per-ring lock.. */ + mutex_lock(&gpu->lock); +diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h +index bc78fb77cc279..768a8dae83c52 100644 +--- a/include/drm/drm_device.h ++++ b/include/drm/drm_device.h +@@ -375,6 +375,13 @@ struct drm_device { + * Root directory for debugfs files. + */ + struct dentry *debugfs_root; ++ ++ /** ++ * @gem_lru_mutex: ++ * ++ * Lock protecting movement of GEM objects between LRUs. ++ */ ++ struct mutex gem_lru_mutex; + }; + + void drm_dev_set_dma_dev(struct drm_device *dev, struct device *dma_dev); +diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h +index 86f5846154f7d..8a704f6a65c15 100644 +--- a/include/drm/drm_gem.h ++++ b/include/drm/drm_gem.h +@@ -245,17 +245,11 @@ struct drm_gem_object_funcs { + * for lockless &shrinker.count_objects, and provides + * &drm_gem_lru_scan for driver's &shrinker.scan_objects + * implementation. ++ * ++ * Any access to this kind of object must be done with ++ * drm_device::gem_lru_mutex held. + */ + struct drm_gem_lru { +- /** +- * @lock: +- * +- * Lock protecting movement of GEM objects between LRUs. All +- * LRUs that the object can move between should be protected +- * by the same lock. +- */ +- struct mutex *lock; +- + /** + * @count: + * +@@ -453,6 +447,9 @@ struct drm_gem_object { + * @lru: + * + * The current LRU list that the GEM object is on. ++ * ++ * Access to this field must be done with drm_device::gem_lru_mutex ++ * held. + */ + struct drm_gem_lru *lru; + }; +@@ -610,12 +607,13 @@ void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count, + int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, + u32 handle, u64 *offset); + +-void drm_gem_lru_init(struct drm_gem_lru *lru, struct mutex *lock); ++void drm_gem_lru_init(struct drm_gem_lru *lru); + void drm_gem_lru_remove(struct drm_gem_object *obj); + void drm_gem_lru_move_tail_locked(struct drm_gem_lru *lru, struct drm_gem_object *obj); + void drm_gem_lru_move_tail(struct drm_gem_lru *lru, struct drm_gem_object *obj); + unsigned long +-drm_gem_lru_scan(struct drm_gem_lru *lru, ++drm_gem_lru_scan(struct drm_device *dev, ++ struct drm_gem_lru *lru, + unsigned int nr_to_scan, + unsigned long *remaining, + bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket), +-- +2.53.0 + diff --git a/queue-7.0/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch b/queue-7.0/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch new file mode 100644 index 0000000000..19e633daed --- /dev/null +++ b/queue-7.0/drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch @@ -0,0 +1,46 @@ +From 65a6cf4b7a029d0ff25da756f0c35909c4ea0871 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 18:02:15 +0530 +Subject: drm/i915/dp: Fix readback for target_rr in Adaptive Sync SDP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ankit Nautiyal + +[ Upstream commit f87abd0c6604fb6cc31cc86fc7ccc6a576924352 ] + +Correct the bit-shift logic to properly readback the 10 bit target_rr from +DB3 and DB4. + +v2: Align the style with readback for vtotal. (Ville) + +Fixes: 12ea89291603 ("drm/i915/dp: Add Read/Write support for Adaptive Sync SDP") +Cc: Mitul Golani +Cc: Ankit Nautiyal +Signed-off-by: Ankit Nautiyal +Reviewed-by: Ville Syrjälä +Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com +(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 2906dc6e630ec..d52205d714eee 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -5067,7 +5067,7 @@ int intel_dp_as_sdp_unpack(struct drm_dp_as_sdp *as_sdp, + as_sdp->length = sdp->sdp_header.HB3 & DP_ADAPTIVE_SYNC_SDP_LENGTH; + as_sdp->mode = sdp->db[0] & DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE; + as_sdp->vtotal = (sdp->db[2] << 8) | sdp->db[1]; +- as_sdp->target_rr = (u64)sdp->db[3] | ((u64)sdp->db[4] & 0x3); ++ as_sdp->target_rr = ((sdp->db[4] & 0x3) << 8) | sdp->db[3]; + as_sdp->target_rr_divider = sdp->db[4] & 0x20 ? true : false; + + return 0; +-- +2.53.0 + diff --git a/queue-7.0/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch b/queue-7.0/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch new file mode 100644 index 0000000000..96edc3f933 --- /dev/null +++ b/queue-7.0/drm-mediatek-mtk_cec-fix-non-static-global-variable.patch @@ -0,0 +1,42 @@ +From 6f18b326188f5893b2b18118e1874dff06413fe7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:59:01 +0200 +Subject: drm/mediatek: mtk_cec: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit 571f00a5fb725984049bd532ee8193cc34ff2994 ] + +The struct 'mtk_cec_driver' is not used outside of the +mtk_cec.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_cec.c:243:24: sparse: warning: symbol +'mtk_cec_driver' was not declared. Should it be static? +``` + +Fixes: 1e914a89ab7e ("drm/mediatek: mtk_cec: Switch to register as module_platform_driver") +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-3-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_cec.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c +index c7be530ca041f..b8ccd6e55bedb 100644 +--- a/drivers/gpu/drm/mediatek/mtk_cec.c ++++ b/drivers/gpu/drm/mediatek/mtk_cec.c +@@ -240,7 +240,7 @@ static const struct of_device_id mtk_cec_of_ids[] = { + }; + MODULE_DEVICE_TABLE(of, mtk_cec_of_ids); + +-struct platform_driver mtk_cec_driver = { ++static struct platform_driver mtk_cec_driver = { + .probe = mtk_cec_probe, + .remove = mtk_cec_remove, + .driver = { +-- +2.53.0 + diff --git a/queue-7.0/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch b/queue-7.0/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch new file mode 100644 index 0000000000..0b016532bd --- /dev/null +++ b/queue-7.0/drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch @@ -0,0 +1,42 @@ +From 53f8b7dff1f2d1dc67a0b710637a7f78064196fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:59:02 +0200 +Subject: drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit 87ed4e845d5a90bba1a56c0a5c580a13982e8648 ] + +The struct 'mtk_hdmi_ddc_driver' is not used outside of the +mtk_hdmi_ddc.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:331:24: sparse: warning: symbol + 'mtk_hdmi_ddc_driver' was not declared. Should it be static? +``` + +Fixes: c241118b6216 ("drm/mediatek: mtk_hdmi_ddc: Switch to register as module_platform_driver") +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-4-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +index 6358e1af69b49..2acbdb025d893 100644 +--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c ++++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +@@ -328,7 +328,7 @@ static const struct of_device_id mtk_hdmi_ddc_match[] = { + }; + MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match); + +-struct platform_driver mtk_hdmi_ddc_driver = { ++static struct platform_driver mtk_hdmi_ddc_driver = { + .probe = mtk_hdmi_ddc_probe, + .remove = mtk_hdmi_ddc_remove, + .driver = { +-- +2.53.0 + diff --git a/queue-7.0/drm-mediatek-mtk_hdmi_ddc_v2-fix-non-static-global-v.patch b/queue-7.0/drm-mediatek-mtk_hdmi_ddc_v2-fix-non-static-global-v.patch new file mode 100644 index 0000000000..9cedaffbd1 --- /dev/null +++ b/queue-7.0/drm-mediatek-mtk_hdmi_ddc_v2-fix-non-static-global-v.patch @@ -0,0 +1,45 @@ +From 906ee70f30df771d76ec01fdf3e929425a3c8a53 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:58:59 +0200 +Subject: drm/mediatek: mtk_hdmi_ddc_v2: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit e9f5e8da29762df1111a58ae0b4a83091595d834 ] + +The struct 'mtk_hdmi_ddc_v2_driver' is not used outside of the +mtk_hdmi_ddc_v2.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c:392:24: sparse: warning: + symbol 'mtk_hdmi_ddc_v2_driver' was not declared. Should it be + static? +``` + +Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188") +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/ +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-1-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c +index d937219fdb7ee..31e81a6de6d85 100644 +--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c ++++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c +@@ -389,7 +389,7 @@ static const struct of_device_id mtk_hdmi_ddc_v2_match[] = { + }; + MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_v2_match); + +-struct platform_driver mtk_hdmi_ddc_v2_driver = { ++static struct platform_driver mtk_hdmi_ddc_v2_driver = { + .probe = mtk_hdmi_ddc_v2_probe, + .driver = { + .name = "mediatek-hdmi-ddc-v2", +-- +2.53.0 + diff --git a/queue-7.0/drm-mediatek-mtk_hdmi_v2-fix-non-static-global-varia.patch b/queue-7.0/drm-mediatek-mtk_hdmi_v2-fix-non-static-global-varia.patch new file mode 100644 index 0000000000..fe2b20f59d --- /dev/null +++ b/queue-7.0/drm-mediatek-mtk_hdmi_v2-fix-non-static-global-varia.patch @@ -0,0 +1,44 @@ +From 3d03ba036d640f30ae3cd1ad4f7899f8696c3c06 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 11:59:00 +0200 +Subject: drm/mediatek: mtk_hdmi_v2: Fix non-static global variable + +From: Louis-Alexis Eyraud + +[ Upstream commit dc245d9a7f1b06f86271d4e524d6e5634c5ce312 ] + +The struct 'mtk_hdmi_v2_clk_names' is not used outside of the +mtk_hdmi_v2.c file, so make it static to silence sparse warning: +``` +drivers/gpu/drm/mediatek/mtk_hdmi_v2.c:53:12: sparse: warning: symbol +'mtk_hdmi_v2_clk_names' was not declared. Should it be static? +``` + +Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188") +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/ +Signed-off-by: Louis-Alexis Eyraud +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-2-d95c4d118b83@collabora.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_hdmi_v2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +index 279ca896b0a2a..6cdad4415475b 100644 +--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c ++++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +@@ -50,7 +50,7 @@ enum mtk_hdmi_v2_clk_id { + MTK_HDMI_V2_CLK_COUNT, + }; + +-const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = { ++static const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = { + [MTK_HDMI_V2_CLK_HDMI_APB_SEL] = "bus", + [MTK_HDMI_V2_CLK_HDCP_SEL] = "hdcp", + [MTK_HDMI_V2_CLK_HDCP_24M_SEL] = "hdcp24m", +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-a6xx-add-soft-fuse-detection-support.patch b/queue-7.0/drm-msm-a6xx-add-soft-fuse-detection-support.patch new file mode 100644 index 0000000000..8d5e9fbc4d --- /dev/null +++ b/queue-7.0/drm-msm-a6xx-add-soft-fuse-detection-support.patch @@ -0,0 +1,206 @@ +From 2e70499bfcdf013c8a4b753c09b90ac4c4b14269 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Mar 2026 05:44:01 +0530 +Subject: drm/msm/a6xx: Add soft fuse detection support + +From: Akhil P Oommen + +[ Upstream commit 4ac686bfd1929ef659a99f893ebe8faf7f35c76c ] + +Recent chipsets like Glymur supports a new mechanism for SKU detection. +A new CX_MISC register exposes the combined (or final) speedbin value +from both HW fuse register and the Soft Fuse register. Implement this new +SKU detection along with a new quirk to identify the GPUs that has soft +fuse support. + +There is a side effect of this patch on A4x and older series. The +speedbin field in the MSM_PARAM_CHIPID will be 0 instead of 0xffff. This +should be okay as Mesa correctly handles it. Speedbin was not even a +thing when those GPUs' support were added. + +Signed-off-by: Akhil P Oommen +Patchwork: https://patchwork.freedesktop.org/patch/714676/ +Message-ID: <20260327-a8xx-gpu-batch2-v2-12-2b53c38d2101@oss.qualcomm.com> +Signed-off-by: Rob Clark +Stable-dep-of: e64bca63647d ("drm/msm/adreno: Fix a reference leak in a6xx_gpu_init()") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 6 +++ + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 41 +++++++++++++++---- + drivers/gpu/drm/msm/adreno/adreno_gpu.c | 5 --- + drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + + drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 4 ++ + 5 files changed, 45 insertions(+), 12 deletions(-) + +diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +index e44302251de56..79acae11154aa 100644 +--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +@@ -1730,6 +1730,7 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) + struct adreno_gpu *adreno_gpu; + struct msm_gpu *gpu; + unsigned int nr_rings; ++ u32 speedbin; + int ret; + + a5xx_gpu = kzalloc_obj(*a5xx_gpu); +@@ -1756,6 +1757,11 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) + return ERR_PTR(ret); + } + ++ /* Set the speedbin value that is passed to userspace */ ++ if (adreno_read_speedbin(&pdev->dev, &speedbin) || !speedbin) ++ speedbin = 0xffff; ++ adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); ++ + msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu, + a5xx_fault_handler); + +diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +index 0e8a48ca816dd..ae61c204898cd 100644 +--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +@@ -2546,13 +2546,33 @@ static u32 fuse_to_supp_hw(const struct adreno_info *info, u32 fuse) + return UINT_MAX; + } + +-static int a6xx_set_supported_hw(struct device *dev, const struct adreno_info *info) ++static int a6xx_read_speedbin(struct device *dev, struct a6xx_gpu *a6xx_gpu, ++ const struct adreno_info *info, u32 *speedbin) ++{ ++ int ret; ++ ++ /* Use speedbin fuse if present. Otherwise, fallback to softfuse */ ++ ret = adreno_read_speedbin(dev, speedbin); ++ if (ret != -ENOENT) ++ return ret; ++ ++ if (info->quirks & ADRENO_QUIRK_SOFTFUSE) { ++ *speedbin = a6xx_llc_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS); ++ *speedbin = A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS_FINALFREQLIMIT(*speedbin); ++ return 0; ++ } ++ ++ return -ENOENT; ++} ++ ++static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu, ++ const struct adreno_info *info) + { + u32 supp_hw; + u32 speedbin; + int ret; + +- ret = adreno_read_speedbin(dev, &speedbin); ++ ret = a6xx_read_speedbin(dev, a6xx_gpu, info, &speedbin); + /* + * -ENOENT means that the platform doesn't support speedbin which is + * fine +@@ -2586,11 +2606,13 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + struct msm_drm_private *priv = dev->dev_private; + struct platform_device *pdev = priv->gpu_pdev; + struct adreno_platform_config *config = pdev->dev.platform_data; ++ const struct adreno_info *info = config->info; + struct device_node *node; + struct a6xx_gpu *a6xx_gpu; + struct adreno_gpu *adreno_gpu; + struct msm_gpu *gpu; + extern int enable_preemption; ++ u32 speedbin; + bool is_a7xx; + int ret, nr_rings = 1; + +@@ -2614,14 +2636,14 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + adreno_gpu->gmu_is_wrapper = of_device_is_compatible(node, "qcom,adreno-gmu-wrapper"); + + adreno_gpu->base.hw_apriv = +- !!(config->info->quirks & ADRENO_QUIRK_HAS_HW_APRIV); ++ !!(info->quirks & ADRENO_QUIRK_HAS_HW_APRIV); + + /* gpu->info only gets assigned in adreno_gpu_init(). A8x is included intentionally */ +- is_a7xx = config->info->family >= ADRENO_7XX_GEN1; ++ is_a7xx = info->family >= ADRENO_7XX_GEN1; + + a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx); + +- ret = a6xx_set_supported_hw(&pdev->dev, config->info); ++ ret = a6xx_set_supported_hw(&pdev->dev, a6xx_gpu, info); + if (ret) { + a6xx_llc_slices_destroy(a6xx_gpu); + kfree(a6xx_gpu); +@@ -2629,15 +2651,20 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + } + + if ((enable_preemption == 1) || (enable_preemption == -1 && +- (config->info->quirks & ADRENO_QUIRK_PREEMPTION))) ++ (info->quirks & ADRENO_QUIRK_PREEMPTION))) + nr_rings = 4; + +- ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, nr_rings); ++ ret = adreno_gpu_init(dev, pdev, adreno_gpu, info->funcs, nr_rings); + if (ret) { + a6xx_destroy(&(a6xx_gpu->base.base)); + return ERR_PTR(ret); + } + ++ /* Set the speedbin value that is passed to userspace */ ++ if (a6xx_read_speedbin(&pdev->dev, a6xx_gpu, info, &speedbin) || !speedbin) ++ speedbin = 0xffff; ++ adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); ++ + /* + * For now only clamp to idle freq for devices where this is known not + * to cause power supply issues: +diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +index 8bf19e72e5bcd..277ef0c5c08d1 100644 +--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +@@ -1182,7 +1182,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, + struct msm_gpu_config adreno_gpu_config = { 0 }; + struct msm_gpu *gpu = &adreno_gpu->base; + const char *gpu_name; +- u32 speedbin; + int ret; + + adreno_gpu->funcs = funcs; +@@ -1211,10 +1210,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, + devm_pm_opp_set_clkname(dev, "core"); + } + +- if (adreno_read_speedbin(dev, &speedbin) || !speedbin) +- speedbin = 0xffff; +- adreno_gpu->speedbin = (uint16_t) (0xffff & speedbin); +- + gpu_name = devm_kasprintf(dev, GFP_KERNEL, "%"ADRENO_CHIPID_FMT, + ADRENO_CHIPID_ARGS(config->chip_id)); + if (!gpu_name) +diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h +index 29097e6b42535..044ed4d49aa7a 100644 +--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h ++++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h +@@ -63,6 +63,7 @@ enum adreno_family { + #define ADRENO_QUIRK_PREEMPTION BIT(5) + #define ADRENO_QUIRK_4GB_VA BIT(6) + #define ADRENO_QUIRK_IFPC BIT(7) ++#define ADRENO_QUIRK_SOFTFUSE BIT(8) + + /* Helper for formating the chip_id in the way that userspace tools like + * crashdec expect. +diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml +index 3941e75107545..2309870f50317 100644 +--- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml ++++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml +@@ -5016,6 +5016,10 @@ by a particular renderpass/blit. + + + ++ ++ ++ ++ + + + +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-a6xx-check-kzalloc-return-in-a8xx_hfi_send_p.patch b/queue-7.0/drm-msm-a6xx-check-kzalloc-return-in-a8xx_hfi_send_p.patch new file mode 100644 index 0000000000..cda4172684 --- /dev/null +++ b/queue-7.0/drm-msm-a6xx-check-kzalloc-return-in-a8xx_hfi_send_p.patch @@ -0,0 +1,40 @@ +From 963bfa2bff5f7a40406eb9f7e5e103c4fb76b5f9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 15:35:58 +0800 +Subject: drm/msm/a6xx: Check kzalloc return in a8xx_hfi_send_perf_table + +From: Chen Ni + +[ Upstream commit b5c7a7f452b885bfbe102bd3a057a5f496802f8b ] + +Check the return value of kzalloc() to prevent a NULL pointer +dereference on allocation failure. + +Fixes: 06cfbca0e1c6 ("drm/msm/a6xx: Share dependency vote table with GMU") +Signed-off-by: Chen Ni +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Akhil P Oommen +Patchwork: https://patchwork.freedesktop.org/patch/721342/ +Message-ID: <20260428073558.1234238-1-nichen@iscas.ac.cn> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +index 4f5dbf46132ba..b40148b754207 100644 +--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c ++++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +@@ -289,6 +289,8 @@ static int a8xx_hfi_send_perf_table(struct a6xx_gmu *gmu) + (gmu->nr_gpu_freqs * num_gx_votes * sizeof(gmu->gx_arc_votes[0])) + + (gmu->nr_gmu_freqs * num_cx_votes * sizeof(gmu->cx_arc_votes[0])); + tbl = kzalloc(size, GFP_KERNEL); ++ if (!tbl) ++ return -ENOMEM; + tbl->type = HFI_TABLE_GPU_PERF; + + /* First fill GX votes */ +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-a6xx-restore-sysprof_active.patch b/queue-7.0/drm-msm-a6xx-restore-sysprof_active.patch new file mode 100644 index 0000000000..e7eff5399e --- /dev/null +++ b/queue-7.0/drm-msm-a6xx-restore-sysprof_active.patch @@ -0,0 +1,46 @@ +From a8b4559b0388fc8de1d9943138740517e2182db6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 11 Apr 2026 08:03:12 -0700 +Subject: drm/msm/a6xx: Restore sysprof_active + +From: Rob Clark + +[ Upstream commit 7a529ff48b99011c946e6d8addd071c06d3ccdae ] + +This got lost in the shuffle somehow when moving the vfunc table to +catalogue. Fixes inhibiting IFPC when userspace is collecting perfcntr +data. + +Fixes: 491fadb2b818 ("drm/msm/adreno: Move adreno_gpu_func to catalogue") +Signed-off-by: Rob Clark +Reviewed-by: Akhil P Oommen +Patchwork: https://patchwork.freedesktop.org/patch/717780/ +Message-ID: <20260411150312.257937-1-robin.clark@oss.qualcomm.com> +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +index 02a776ac9ab43..3e26b60d7f678 100644 +--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +@@ -2725,6 +2725,7 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = { + .create_private_vm = a6xx_create_private_vm, + .get_rptr = a6xx_get_rptr, + .progress = a6xx_progress, ++ .sysprof_setup = a6xx_gmu_sysprof_setup, + }, + .init = a6xx_gpu_init, + .get_timestamp = a6xx_gmu_get_timestamp, +@@ -2793,6 +2794,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = { + .create_private_vm = a6xx_create_private_vm, + .get_rptr = a6xx_get_rptr, + .progress = a6xx_progress, ++ .sysprof_setup = a6xx_gmu_sysprof_setup, + }, + .init = a6xx_gpu_init, + .get_timestamp = a6xx_gmu_get_timestamp, +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-adreno-fix-a-reference-leak-in-a6xx_gpu_init.patch b/queue-7.0/drm-msm-adreno-fix-a-reference-leak-in-a6xx_gpu_init.patch new file mode 100644 index 0000000000..b352ae5461 --- /dev/null +++ b/queue-7.0/drm-msm-adreno-fix-a-reference-leak-in-a6xx_gpu_init.patch @@ -0,0 +1,59 @@ +From 3668863400987b432eed6469d699b4601cf23f5c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 24 Jan 2026 00:37:38 +0800 +Subject: drm/msm/adreno: Fix a reference leak in a6xx_gpu_init() + +From: Felix Gu + +[ Upstream commit e64bca63647db1d5518198d6c5ca2dbcc66b182b ] + +In a6xx_gpu_init(), node is obtained via of_parse_phandle(). +While there was a manual of_node_put() at the end of the +common path, several early error returns would bypass this call, +resulting in a reference leak. +Fix this by using the __free(device_node) cleanup handler to +release the reference when the variable goes out of scope. + +Fixes: 5a903a44a984 ("drm/msm/a6xx: Introduce GMU wrapper support") +Signed-off-by: Felix Gu +Patchwork: https://patchwork.freedesktop.org/patch/700661/ +Message-ID: <20260124-a6xx_gpu-v1-1-fa0c8b2dcfb1@gmail.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +index ae61c204898cd..02a776ac9ab43 100644 +--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +@@ -2607,7 +2607,6 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + struct platform_device *pdev = priv->gpu_pdev; + struct adreno_platform_config *config = pdev->dev.platform_data; + const struct adreno_info *info = config->info; +- struct device_node *node; + struct a6xx_gpu *a6xx_gpu; + struct adreno_gpu *adreno_gpu; + struct msm_gpu *gpu; +@@ -2629,7 +2628,8 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + adreno_gpu->registers = NULL; + + /* Check if there is a GMU phandle and set it up */ +- node = of_parse_phandle(pdev->dev.of_node, "qcom,gmu", 0); ++ struct device_node *node __free(device_node) = ++ of_parse_phandle(pdev->dev.of_node, "qcom,gmu", 0); + /* FIXME: How do we gracefully handle this? */ + BUG_ON(!node); + +@@ -2676,7 +2676,6 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) + ret = a6xx_gmu_wrapper_init(a6xx_gpu, node); + else + ret = a6xx_gmu_init(a6xx_gpu, node); +- of_node_put(node); + if (ret) { + a6xx_destroy(&(a6xx_gpu->base.base)); + return ERR_PTR(ret); +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch b/queue-7.0/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch new file mode 100644 index 0000000000..9c0f2d0490 --- /dev/null +++ b/queue-7.0/drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch @@ -0,0 +1,55 @@ +From 4d5d7916403eaf7ffe1ae790434c739facb9d0a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 11 Apr 2026 17:59:15 +0300 +Subject: drm/msm/adreno: fix userspace-triggered crash on a2xx-a4xx + +From: Dmitry Baryshkov + +[ Upstream commit 2b4abf879360ea00a9e2b46d2d15dcdbc0687eed ] + +Before a5xx Adreno driver will not try fetching UBWC params (because +those generations didn't support UBWC anyway), however it's still +possible to query UBWC-related params from the userspace, triggering +possible NULL pointer dereference. Check for UBWC config in +adreno_get_param() and return sane defaults if there is none. + +Fixes: a452510aad53 ("drm/msm/adreno: Switch to the common UBWC config struct") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Rob Clark +Patchwork: https://patchwork.freedesktop.org/patch/717778/ +Message-ID: <20260411-adreno-fix-ubwc-v3-1-4983156f3f80@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +index 277ef0c5c08d1..682a09a376fbf 100644 +--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +@@ -424,15 +424,21 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, + *value = vm->mm_range; + return 0; + case MSM_PARAM_HIGHEST_BANK_BIT: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->highest_bank_bit; + return 0; + case MSM_PARAM_RAYTRACING: + *value = adreno_gpu->has_ray_tracing; + return 0; + case MSM_PARAM_UBWC_SWIZZLE: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->ubwc_swizzle; + return 0; + case MSM_PARAM_MACROTILE_MODE: ++ if (!adreno_gpu->ubwc_config) ++ return UERR(ENOENT, drm, "no UBWC on this platform"); + *value = adreno_gpu->ubwc_config->macrotile_mode; + return 0; + case MSM_PARAM_UCHE_TRAP_BASE: +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch b/queue-7.0/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch new file mode 100644 index 0000000000..14c66fd0f8 --- /dev/null +++ b/queue-7.0/drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch @@ -0,0 +1,53 @@ +From 5b5e3a09354d47cbb460cf1b511352dac0d0223c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 03:24:58 +0300 +Subject: drm/msm/dpu: don't mix devm and drmm functions + +From: Dmitry Baryshkov + +[ Upstream commit c0c70a11365cba7fba25a77463582bcec0f7846e ] + +Mixing devm and drmm functions will result in a use-after-free on msm +driver teardown if userspace keeps a reference on the drm device: +The WB connector data will be destroyed because of the use of +devm_kzalloc()), while the usersoace still can try interacting with the +WB connector (which uses drmm_ functions). + +Change dpu_writeback_init() to use drmm_. + +Fixes: 0b37ac63fc9d ("drm/msm/dpu: use drmm_writeback_connector_init()") +Reported-by: Christophe JAILLET +Closes: https://lore.kernel.org/r/78c764b8-44cf-4db5-88e7-807a85954518@wanadoo.fr +Signed-off-by: Dmitry Baryshkov +Reviewed-by: John.Harrison@Igalia.com +Patchwork: https://patchwork.freedesktop.org/patch/722656/ +Link: https://lore.kernel.org/r/20260505-wb-drop-encoder-v5-1-42567b7c7af2@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +index 7545c0293efbd..6f2370c9dd988 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #include "dpu_writeback.h" + +@@ -125,7 +126,7 @@ int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, + struct dpu_wb_connector *dpu_wb_conn; + int rc = 0; + +- dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL); ++ dpu_wb_conn = drmm_kzalloc(dev, sizeof(*dpu_wb_conn), GFP_KERNEL); + if (!dpu_wb_conn) + return -ENOMEM; + +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-dpu-fix-kaanapali-cwb-register-configuration.patch b/queue-7.0/drm-msm-dpu-fix-kaanapali-cwb-register-configuration.patch new file mode 100644 index 0000000000..3617d4f878 --- /dev/null +++ b/queue-7.0/drm-msm-dpu-fix-kaanapali-cwb-register-configuration.patch @@ -0,0 +1,47 @@ +From 5397342f97df39c1fedff93365ba6e0c231f9ca9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:14:25 +0530 +Subject: drm/msm/dpu: Fix Kaanapali CWB register configuration + +From: Mahadevan P + +[ Upstream commit d03279f0d9fdbe6f6761f191a76093c395930018 ] + +The Kaanapali DPU catalog defines kaanapali_cwb[] with the correct +CWB base addresses for this platform (0x169200, 0x169600, 0x16a200, +0x16a600), but the dpu_kaanapali_cfg struct was mistakenly pointing +to sm8650_cwb instead. The SM8650 CWB blocks sit at completely +different offsets (0x66200, 0x66600, 0x7E200, 0x7E600), so using +them on Kaanapali would program CWB registers at wrong addresses, +corrupting unrelated hardware blocks and breaking writeback capture. + +Fix this by pointing .cwb to the correct kaanapali_cwb array. + +Fixes: 83fe2cd56b1d ("drm/msm/dpu: Add support for Kaanapali DPU") +Signed-off-by: Mahadevan P +Reviewed-by: Konrad Dybcio +Reviewed-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/721444/ +Link: https://lore.kernel.org/r/20260428-kaanapali_cwb-v1-1-51fdb2c65498@oss.qualcomm.com +Signed-off-by: Dmitry Baryshkov +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +index 0b20401b04cf0..e3c47b6702f18 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h ++++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +@@ -481,7 +481,7 @@ const struct dpu_mdss_cfg dpu_kaanapali_cfg = { + .wb_count = ARRAY_SIZE(kaanapali_wb), + .wb = kaanapali_wb, + .cwb_count = ARRAY_SIZE(kaanapali_cwb), +- .cwb = sm8650_cwb, ++ .cwb = kaanapali_cwb, + .intf_count = ARRAY_SIZE(kaanapali_intf), + .intf = kaanapali_intf, + .vbif_count = ARRAY_SIZE(sm8650_vbif), +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-dpu-fix-uv-scanlines-calculation-for-yuv-ubw.patch b/queue-7.0/drm-msm-dpu-fix-uv-scanlines-calculation-for-yuv-ubw.patch new file mode 100644 index 0000000000..1f04e5d2e9 --- /dev/null +++ b/queue-7.0/drm-msm-dpu-fix-uv-scanlines-calculation-for-yuv-ubw.patch @@ -0,0 +1,71 @@ +From d0860ae6ba5d858af3f44392e3b0f7be54aa49ba Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Apr 2026 17:14:30 +0200 +Subject: drm/msm/dpu: fix UV scanlines calculation for YUV UBWC formats + +From: Neil Armstrong + +[ Upstream commit 933430f1709b089a0bf0b23ef0f047014ef899e7 ] + +The UV scanlines is calculated with (height + 1) / 2 unlike +the Y scanlines, add back the correct scanlines calculation +for UBWC YUV formats. + +Fixes: 2f3ff6ab8f5c ("drm/msm/dpu: use standard functions in _dpu_format_populate_plane_sizes_ubwc()") +Fixes: ada4a19ed21c ("drm/msm/dpu: rewrite _dpu_format_populate_plane_sizes_ubwc()") +Signed-off-by: Neil Armstrong +Reviewed-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/718309/ +Link: https://lore.kernel.org/r/20260414-topic-sm8x50-msm-dpu1-formats-qc10c-v1-1-0b62325b9030@linaro.org +Signed-off-by: Dmitry Baryshkov +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c +index 6e8883dbfad43..590922c4f69bf 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c +@@ -61,7 +61,7 @@ static int _dpu_format_populate_plane_sizes_ubwc( + bool meta = MSM_FORMAT_IS_UBWC(fmt); + + if (MSM_FORMAT_IS_YUV(fmt)) { +- unsigned int stride, sclines; ++ unsigned int stride, y_sclines, uv_sclines; + unsigned int y_tile_width, y_tile_height; + unsigned int y_meta_stride, y_meta_scanlines; + unsigned int uv_meta_stride, uv_meta_scanlines; +@@ -77,23 +77,25 @@ static int _dpu_format_populate_plane_sizes_ubwc( + y_tile_width = 32; + } + +- sclines = round_up(fb->height, 16); ++ y_sclines = round_up(fb->height, 16); ++ uv_sclines = round_up((fb->height+1)>>1, 16); + y_tile_height = 4; + } else { + stride = round_up(fb->width, 128); + y_tile_width = 32; + +- sclines = round_up(fb->height, 32); ++ y_sclines = round_up(fb->height, 32); ++ uv_sclines = round_up((fb->height+1)>>1, 32); + y_tile_height = 8; + } + + layout->plane_pitch[0] = stride; + layout->plane_size[0] = round_up(layout->plane_pitch[0] * +- sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); ++ y_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); + + layout->plane_pitch[1] = stride; + layout->plane_size[1] = round_up(layout->plane_pitch[1] * +- sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); ++ uv_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); + + if (!meta) + return 0; +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch b/queue-7.0/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch new file mode 100644 index 0000000000..984a81c92b --- /dev/null +++ b/queue-7.0/drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch @@ -0,0 +1,50 @@ +From 1bfdaa10b1581b2eef0a7334e507861adfa0d1c6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 20:21:38 +0300 +Subject: drm/msm/dsi: don't dump registers past the mapped region + +From: Dmitry Baryshkov + +[ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] + +On DSI 6G platforms the IO address space is internally adjusted by +io_offset. Later this adjusted address might be used for memory dumping. +However the size that is used for memory dumping isn't adjusted to +account for the io_offset, leading to the potential access to the +unmapped region. Lower ctrl_size by the io_offset value to prevent +access past the mapped area. + + msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) + msm_dsi_host_snapshot+0x4c/0x78 [msm] + msm_dsi_snapshot+0x28/0x50 [msm] + msm_disp_snapshot_capture_state+0x74/0x140 [msm] + msm_disp_snapshot_state_sync+0x60/0x90 [msm] + _msm_disp_snapshot_work+0x30/0x90 [msm] + kthread_worker_fn+0xdc/0x460 + kthread+0x120/0x140 + +Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") +Signed-off-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Patchwork: https://patchwork.freedesktop.org/patch/721747/ +Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c +index 1c0841a1c1013..50474c994d473 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_host.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_host.c +@@ -2003,6 +2003,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) + + /* fixup base address by io offset */ + msm_host->ctrl_base += cfg->io_offset; ++ msm_host->ctrl_size -= cfg->io_offset; + + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-fix-gmem_base-for-a650.patch b/queue-7.0/drm-msm-fix-gmem_base-for-a650.patch new file mode 100644 index 0000000000..cf301f34d0 --- /dev/null +++ b/queue-7.0/drm-msm-fix-gmem_base-for-a650.patch @@ -0,0 +1,47 @@ +From 9cc33bcb304438e1d05ede668132e671736d1be8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 14 Mar 2026 04:14:50 +0000 +Subject: drm/msm: Fix GMEM_BASE for A650 + +From: Alexander Koskovich + +[ Upstream commit 46e351e84853dda726072bb3d38ba7bd63e7532b ] + +Commit dc220915ddb2 ("drm/msm: Fix GMEM_BASE for gen8") changed the +GMEM_BASE check from adreno_is_a650_family() & adreno_is_a740_family() +to family >= ADRENO_6XX_GEN4. + +This inadvertently excluded A650 (ADRENO_6XX_GEN3), causing it to report +an incorrect GMEM_BASE which results in severe rendering corruption. + +Update check to also include ADRENO_6XX_GEN3 to fix A650. + +Fixes: dc220915ddb2 ("drm/msm: Fix GMEM_BASE for gen8") +Signed-off-by: Alexander Koskovich +Reviewed-by: Konrad Dybcio +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Akhil P Oommen +Patchwork: https://patchwork.freedesktop.org/patch/711880/ +Message-ID: <20260314-fix-gmem-base-a650-v1-1-3308f60cf74c@pm.me> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +index 785e99fb5bd5d..8bf19e72e5bcd 100644 +--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c +@@ -376,7 +376,7 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, + *value = adreno_gpu->info->gmem; + return 0; + case MSM_PARAM_GMEM_BASE: +- if (adreno_gpu->info->family >= ADRENO_6XX_GEN4) ++ if (adreno_gpu->info->family >= ADRENO_6XX_GEN3) + *value = 0; + else + *value = 0x100000; +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch b/queue-7.0/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch new file mode 100644 index 0000000000..68ded3b1d6 --- /dev/null +++ b/queue-7.0/drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch @@ -0,0 +1,52 @@ +From 7ea136bb54ed25892475a28ee1d1c01c29c86d3b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 13:02:38 +0900 +Subject: drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN + +From: Mikko Perttunen + +[ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] + +Commit "iommu: return full error code from iommu_map_sg[_atomic]()" +changed iommu_map_sgtable() to return an ssize_t and negative values +in error cases, rather than a size_t and a zero. + +Store the return value in the appropriate type and in case of error, +return it rather than WARNing. + +Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") +Signed-off-by: Mikko Perttunen +Patchwork: https://patchwork.freedesktop.org/patch/719685/ +Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c +index 7d449e5202c5d..058c71c82cf54 100644 +--- a/drivers/gpu/drm/msm/msm_iommu.c ++++ b/drivers/gpu/drm/msm/msm_iommu.c +@@ -677,7 +677,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + int prot) + { + struct msm_iommu *iommu = to_msm_iommu(mmu); +- size_t ret; ++ ssize_t ret; + + WARN_ON(off != 0); + +@@ -686,7 +686,8 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, + iova |= GENMASK_ULL(63, 49); + + ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); +- WARN_ON(!ret); ++ if (ret < 0) ++ return ret; + + return (ret == len) ? 0 : -EINVAL; + } +-- +2.53.0 + diff --git a/queue-7.0/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch b/queue-7.0/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch new file mode 100644 index 0000000000..bd632ae30c --- /dev/null +++ b/queue-7.0/drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch @@ -0,0 +1,104 @@ +From d94a33d019f0a9bfd57a0d1b33357956ed1b6ba9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:53:45 +0300 +Subject: drm/msm/snapshot: fix dumping of the unaligned regions + +From: Dmitry Baryshkov + +[ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] + +The snapshotting code internally aligns data segment to 16 bytes. This +works fine for DPU code (where most of the regions are aligned), but +fails for snapshotting of the DSI data (because DSI data region is +shifted by 4 bytes). Fix the code by removing length alignment and by +accurately printing last registers in the region. While reworking the +code also fix the 16x memory overallocation in +msm_disp_state_dump_regs(). + +Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") +Reported-by: Salendarsingh Gaud +Signed-off-by: Dmitry Baryshkov +Patchwork: https://patchwork.freedesktop.org/patch/725449/ +Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> +Signed-off-by: Rob Clark +Signed-off-by: Sasha Levin +--- + .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +index 427d3ee2b8337..6e0f8671bfb46 100644 +--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c ++++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +@@ -9,7 +9,7 @@ + + #include "msm_disp_snapshot.h" + +-static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) ++static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) + { + u32 len_padded; + u32 num_rows; +@@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + void __iomem *end_addr; + int i; + +- len_padded = aligned_len * REG_DUMP_ALIGN; +- num_rows = aligned_len / REG_DUMP_ALIGN; ++ len_padded = round_up(len, REG_DUMP_ALIGN); ++ num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); + + addr = base_addr; +- end_addr = base_addr + aligned_len; ++ end_addr = base_addr + len; + + *reg = kvzalloc(len_padded, GFP_KERNEL); + if (!*reg) +@@ -48,8 +48,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b + static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + void __iomem *base_addr, struct drm_printer *p) + { ++ void __iomem *addr, *end_addr; + int i; +- void __iomem *addr; + u32 num_rows; + + if (!dump_addr) { +@@ -58,6 +58,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + } + + addr = base_addr; ++ end_addr = base_addr + len; + num_rows = len / REG_DUMP_ALIGN; + + for (i = 0; i < num_rows; i++) { +@@ -67,6 +68,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, + dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); + addr += REG_DUMP_ALIGN; + } ++ ++ if (addr != end_addr) { ++ drm_printf(p, "0x%lx : %08x", ++ (unsigned long)(addr - base_addr), ++ dump_addr[i * 4]); ++ if (addr + 0x4 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 1]); ++ if (addr + 0x8 < end_addr) ++ drm_printf(p, " %08x", dump_addr[i * 4 + 2]); ++ drm_printf(p, "\n"); ++ } + } + + void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) +@@ -185,7 +197,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, + va_end(va); + + INIT_LIST_HEAD(&new_blk->node); +- new_blk->size = ALIGN(len, REG_DUMP_ALIGN); ++ new_blk->size = len; + new_blk->base_addr = base_addr; + + msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1401998.patch b/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1401998.patch new file mode 100644 index 0000000000..ff9f1b45c6 --- /dev/null +++ b/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1401998.patch @@ -0,0 +1,61 @@ +From 67f19dca447bdb3b9826a64785097e7c6a5035ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 09:27:40 -0800 +Subject: drm/xe: Consolidate workaround entries for Wa_14019988906 + +From: Matt Roper + +[ Upstream commit c2142a1a841525d897ef69b3e6a5ab48183e1fcf ] + +Wa_14019988906 applies to all graphics versions from 20.01 through 20.04 +(inclusive). Consolidate the RTP entries into a single range-based entry. + +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260220-forupstream-wa_cleanup-v2-18-b12005a05af6@intel.com +Signed-off-by: Matt Roper +Stable-dep-of: a4660bd94973 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_wa.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c +index 9ddd21a21dcef..c436d0ad51e77 100644 +--- a/drivers/gpu/drm/xe/xe_wa.c ++++ b/drivers/gpu/drm/xe/xe_wa.c +@@ -716,6 +716,10 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE)) + }, ++ { XE_RTP_NAME("14019988906"), ++ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)), ++ XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) ++ }, + + /* DG1 */ + +@@ -772,10 +776,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + + /* Xe2_LPG */ + +- { XE_RTP_NAME("14019988906"), +- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) +- }, + { XE_RTP_NAME("18033852989"), + XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) +@@ -810,10 +810,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS)) + }, +- { XE_RTP_NAME("14019988906"), +- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) +- }, + { XE_RTP_NAME("14021490052"), + XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(FF_MODE, +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1803385.patch b/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1803385.patch new file mode 100644 index 0000000000..4d06dbcc7e --- /dev/null +++ b/queue-7.0/drm-xe-consolidate-workaround-entries-for-wa_1803385.patch @@ -0,0 +1,61 @@ +From 60ded77fbfee61cc2f6f4808f8fb9f37b83a1c68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 09:27:41 -0800 +Subject: drm/xe: Consolidate workaround entries for Wa_18033852989 + +From: Matt Roper + +[ Upstream commit fe681e7b44d78fd77d79de21eca58c3b6bdcda0e ] + +Wa_18033852989 applies to all graphics versions from 20.01 through 20.04 +(inclusive). Consolidate the RTP entries into a single range-based entry. + +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260220-forupstream-wa_cleanup-v2-19-b12005a05af6@intel.com +Signed-off-by: Matt Roper +Stable-dep-of: a4660bd94973 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_wa.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c +index c436d0ad51e77..6e3c44bc69daf 100644 +--- a/drivers/gpu/drm/xe/xe_wa.c ++++ b/drivers/gpu/drm/xe/xe_wa.c +@@ -720,6 +720,10 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) + }, ++ { XE_RTP_NAME("18033852989"), ++ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)), ++ XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) ++ }, + + /* DG1 */ + +@@ -776,10 +780,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + + /* Xe2_LPG */ + +- { XE_RTP_NAME("18033852989"), +- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) +- }, + { XE_RTP_NAME("14021567978"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED), + ENGINE_CLASS(RENDER)), +@@ -827,10 +827,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE)) + }, +- { XE_RTP_NAME("18033852989"), +- XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) +- }, + + /* Xe3_LPG */ + { XE_RTP_NAME("14021490052"), +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch b/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch new file mode 100644 index 0000000000..69473e72b2 --- /dev/null +++ b/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch @@ -0,0 +1,57 @@ +From 499fdec8835dd27880cf1ec7882d04d4816e352e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 18:44:45 -0300 +Subject: drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1 + +From: Gustavo Sousa + +[ Upstream commit a4660bd949733fd6ea621fdb50fabac2608155e9 ] + +The register COMMON_SLICE_CHICKEN1 is a MCR register on Xe2. +Let's make sure to define a MCR version of it and use it for the +relevant IP versions. + +Use XEHP_ as prefix for the register name, since it is MCR as of Xe_HP. + +Fixes: a5d221924e13 ("drm/xe/xe2_hpg: Add set of workarounds") +Fixes: 9f18b55b6d3f ("drm/xe/xe2: Add workaround 18033852989") +Bspec: 66534, 71185 +Reviewed-by: Matt Roper +Link: https://patch.msgid.link/20260514-rtp-mcr-check-v3-2-30dd47855fee@intel.com +Signed-off-by: Gustavo Sousa +(cherry picked from commit a672725fdbfc3ea430130039d677c7dc98d59df8) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/regs/xe_gt_regs.h | 1 + + drivers/gpu/drm/xe/xe_wa.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +index 9d66f168ab8a7..dd06487c87edc 100644 +--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h ++++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +@@ -145,6 +145,7 @@ + #define MSAA_OPTIMIZATION_REDUC_DISABLE REG_BIT(11) + + #define COMMON_SLICE_CHICKEN1 XE_REG(0x7010, XE_REG_OPTION_MASKED) ++#define XEHP_COMMON_SLICE_CHICKEN1 XE_REG_MCR(0x7010, XE_REG_OPTION_MASKED) + #define DISABLE_BOTTOM_CLIP_RECTANGLE_TEST REG_BIT(14) + + #define HIZ_CHICKEN XE_REG(0x7018, XE_REG_OPTION_MASKED) +diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c +index 6e3c44bc69daf..4ecf96fb40846 100644 +--- a/drivers/gpu/drm/xe/xe_wa.c ++++ b/drivers/gpu/drm/xe/xe_wa.c +@@ -722,7 +722,7 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + }, + { XE_RTP_NAME("18033852989"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) ++ XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) + }, + + /* DG1 */ +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch-29026 b/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch-29026 new file mode 100644 index 0000000000..6f042af1b3 --- /dev/null +++ b/queue-7.0/drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch-29026 @@ -0,0 +1,86 @@ +From d4b96e3d78817c2f823b9b178ce6cdecd986d085 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 18:44:46 -0300 +Subject: drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN4 + +From: Gustavo Sousa + +[ Upstream commit 6df5678b6a94ac80e31e847074c4b30c21025b1f ] + +The register COMMON_SLICE_CHICKEN4 is a MCR register on both Xe2 and +Xe3. Let's make sure to define a MCR version of it and use it for the +relevant IP versions. + +Use XEHP_ as prefix for the register name, since it is MCR as of Xe_HP. + +v2: + - Also change for one entry in lrc_tunnings, which was caught by + manual testing and add corresponging Fixes tag in commit message. + (Gustavo) + +Fixes: 8d6f16f1f082 ("drm/xe: Extend Wa_22021007897 to Xe3 platforms") +Fixes: e5c13e2c505b ("drm/xe/xe2hpg: Add Wa_22021007897") +Fixes: 8ccf5f6b2295 ("drm/xe/tuning: Apply windower hardware filtering setting on Xe3 and Xe3p") +Bspec: 66534, 71185, 74417 +Reviewed-by: Matt Roper +Link: https://patch.msgid.link/20260514-rtp-mcr-check-v3-3-30dd47855fee@intel.com +Signed-off-by: Gustavo Sousa +(cherry picked from commit 75f65f1a4c06da1d87f28570a9d4cdad28f13360) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/regs/xe_gt_regs.h | 1 + + drivers/gpu/drm/xe/xe_tuning.c | 2 +- + drivers/gpu/drm/xe/xe_wa.c | 4 ++-- + 3 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +index e9a82029f5066..bdbcbccd759e2 100644 +--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h ++++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +@@ -168,6 +168,7 @@ + #define XEHPG_SC_INSTDONE_EXTRA2 XE_REG_MCR(0x7108) + + #define COMMON_SLICE_CHICKEN4 XE_REG(0x7300, XE_REG_OPTION_MASKED) ++#define XEHP_COMMON_SLICE_CHICKEN4 XE_REG_MCR(0x7300, XE_REG_OPTION_MASKED) + #define SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE REG_BIT(12) + #define DISABLE_TDC_LOAD_BALANCING_CALC REG_BIT(6) + #define HW_FILTERING REG_BIT(5) +diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c +index 314cbe70d2f2a..e15553bfb7391 100644 +--- a/drivers/gpu/drm/xe/xe_tuning.c ++++ b/drivers/gpu/drm/xe/xe_tuning.c +@@ -112,7 +112,7 @@ static const struct xe_rtp_entry_sr engine_tunings[] = { + static const struct xe_rtp_entry_sr lrc_tunings[] = { + { XE_RTP_NAME("Tuning: Windower HW Filtering"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3599), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, HW_FILTERING)) ++ XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN4, HW_FILTERING)) + }, + + /* DG2 */ +diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c +index 4ecf96fb40846..dce0a39d19146 100644 +--- a/drivers/gpu/drm/xe/xe_wa.c ++++ b/drivers/gpu/drm/xe/xe_wa.c +@@ -825,7 +825,7 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + }, + { XE_RTP_NAME("22021007897"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE)) ++ XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE)) + }, + + /* Xe3_LPG */ +@@ -841,7 +841,7 @@ static const struct xe_rtp_entry_sr lrc_was[] = { + }, + { XE_RTP_NAME("22021007897"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)), +- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE)) ++ XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE)) + }, + { XE_RTP_NAME("14024681466"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)), +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch b/queue-7.0/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch new file mode 100644 index 0000000000..42f528d523 --- /dev/null +++ b/queue-7.0/drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch @@ -0,0 +1,56 @@ +From 0bbf1af3a125df2f579292f2979872ab3c0c3d25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 15:41:34 +0000 +Subject: drm/xe/gsc: Fix double-free of managed BO in error path + +From: Shuicheng Lin + +[ Upstream commit d3ded53fab90996e7d94a39049e11962dd066725 ] + +The error path in xe_gsc_init_post_hwconfig() explicitly frees a BO +allocated with xe_managed_bo_create_pin_map() via +xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm +cleanup action registered, this causes a double-free when devm +unwinds during probe failure. + +Remove the explicit free and let devm handle it, consistent with +all other xe_managed_bo_create_pin_map() callers. + +Fixes: 2e5d47fe7839 ("drm/xe/uc: Use managed bo for HuC and GSC objects") +Reviewed-by: Daniele Ceraolo Spurio +Assisted-by: Claude:claude-opus-4.6 +Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gsc.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c +index 0d13e357fb43c..aab59dc647fbd 100644 +--- a/drivers/gpu/drm/xe/xe_gsc.c ++++ b/drivers/gpu/drm/xe/xe_gsc.c +@@ -482,8 +482,7 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + EXEC_QUEUE_FLAG_PERMANENT, 0); + if (IS_ERR(q)) { + xe_gt_err(gt, "Failed to create queue for GSC submission\n"); +- err = PTR_ERR(q); +- goto out_bo; ++ return PTR_ERR(q); + } + + wq = alloc_ordered_workqueue("gsc-ordered-wq", 0); +@@ -506,8 +505,6 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) + + out_q: + xe_exec_queue_put(q); +-out_bo: +- xe_bo_unpin_map_no_vm(bo); + return err; + } + +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch b/queue-7.0/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch new file mode 100644 index 0000000000..51a17b41c9 --- /dev/null +++ b/queue-7.0/drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch @@ -0,0 +1,55 @@ +From e3de313e45bafb4b89cdfcacce2c57113abeeb95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 20:32:10 +0000 +Subject: drm/xe/oa: Fix exec_queue leak on width check in stream open + +From: Shuicheng Lin + +[ Upstream commit 4d25342543c01310fc4e0cba7cb17c775e2421e2 ] + +In xe_oa_stream_open_ioctl(), when param.exec_q->width > 1 the +function returns -EOPNOTSUPP directly, skipping the existing +err_exec_q cleanup path. The exec_queue reference obtained by +xe_exec_queue_lookup() is leaked. + +The exec queue holds a reference on the xe_file, which is only +dropped during queue teardown. The leaked lookup ref is not on +the file's exec_queue xarray, so file close cannot release it. +This keeps both the exec queue and the file private state pinned +indefinitely. + +Jump to err_exec_q instead of returning directly so the reference +is released. + +Fixes: f0ed39830e60 ("xe/oa: Fix query mode of operation for OAR/OAC") +Assisted-by: Claude:claude-opus-4.6 +Reviewed-by: Ashutosh Dixit +Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com +Signed-off-by: Shuicheng Lin +(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c +index fa90441d30529..449a431ec1d4e 100644 +--- a/drivers/gpu/drm/xe/xe_oa.c ++++ b/drivers/gpu/drm/xe/xe_oa.c +@@ -2048,8 +2048,10 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f + if (XE_IOCTL_DBG(oa->xe, !param.exec_q)) + return -ENOENT; + +- if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) +- return -EOPNOTSUPP; ++ if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) { ++ ret = -EOPNOTSUPP; ++ goto err_exec_q; ++ } + } + + /* +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch b/queue-7.0/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch new file mode 100644 index 0000000000..c3f343e389 --- /dev/null +++ b/queue-7.0/drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch @@ -0,0 +1,76 @@ +From 26428b3f576f3ae8caa28f14127ecad41a81b1be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 23:19:18 +0530 +Subject: drm/xe/pf: Fix CFI failure in debugfs access + +From: Mohanram Meenakshisundaram + +[ Upstream commit 96bf49b526e2d03a2b7f6e861925a08f46ed0d28 ] + +Reading debugfs file (/sys/kernel/debug/dri/0/gt*/pf/adverse_events) +with CFI (Control Flow Integrity) enabled, the kernel panics at +xe_gt_debugfs_simple_show+0x82/0xc0. + +xe_gt_debugfs_simple_show() declare a function pointer expecting int +return type, but xe_gt_sriov_pf_monitor_print_events() is void return +type, leading to CFI failure and kernel panic. + +[507620.973657] CFI failure at xe_gt_debugfs_simple_show+0x82/0xc0 [xe] +(target: xe_gt_sriov_pf_monitor_print_events+0x0/0x130 [xe]; expected +type: 0xd72c7139) + +Fix xe_gt_sriov_pf_monitor_print_events() function by updating to return +an int type. + +Fixes: 1c99d3d3edab ("drm/xe/pf: Expose PF monitor details via debugfs") +Signed-off-by: Mohanram Meenakshisundaram +Reviewed-by: Michal Wajdeczko +Signed-off-by: Michal Wajdeczko +Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com +(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c | 6 +++++- + drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h | 2 +- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +index 7d532bded02a8..a85ba44353789 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +@@ -114,8 +114,10 @@ int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 + * VFs with no events are not printed. + * + * This function can only be called on PF. ++ * ++ * Return: always 0 + */ +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) + { + unsigned int n, total_vfs = xe_gt_sriov_pf_get_totalvfs(gt); + const struct xe_gt_sriov_monitor *data; +@@ -144,4 +146,6 @@ void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p + #undef __format + #undef __value + } ++ ++ return 0; + } +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +index 7ca9351a271b7..0b8f088d3a16a 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +@@ -13,7 +13,7 @@ struct drm_printer; + struct xe_gt; + + void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid); +-void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); + + #ifdef CONFIG_PCI_IOV + int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len); +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-tuning-apply-windower-hardware-filtering-sett.patch b/queue-7.0/drm-xe-tuning-apply-windower-hardware-filtering-sett.patch new file mode 100644 index 0000000000..c9beff2768 --- /dev/null +++ b/queue-7.0/drm-xe-tuning-apply-windower-hardware-filtering-sett.patch @@ -0,0 +1,57 @@ +From 4699c185832a5166da3b10681aa30fe337d4b70a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Feb 2026 15:50:56 -0800 +Subject: drm/xe/tuning: Apply windower hardware filtering setting on Xe3 and + Xe3p + +From: Matt Roper + +[ Upstream commit 8ccf5f6b2295164962bbee5b0770f4366fd9bee2 ] + +A recent bspec tuning guide update asks us to program +COMMON_SLICE_CHICKEN4[5] on Xe3 and Xe3p platforms. Add this setting to +our LRC tuning RTP table so that the setting will become part of each +context's LRC. + +Bspec: 72161, 55902 +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260224235055.3038710-2-matthew.d.roper@intel.com +Signed-off-by: Matt Roper +Stable-dep-of: 6df5678b6a94 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN4") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/regs/xe_gt_regs.h | 1 + + drivers/gpu/drm/xe/xe_tuning.c | 5 +++++ + 2 files changed, 6 insertions(+) + +diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +index dd06487c87edc..e9a82029f5066 100644 +--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h ++++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h +@@ -170,6 +170,7 @@ + #define COMMON_SLICE_CHICKEN4 XE_REG(0x7300, XE_REG_OPTION_MASKED) + #define SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE REG_BIT(12) + #define DISABLE_TDC_LOAD_BALANCING_CALC REG_BIT(6) ++#define HW_FILTERING REG_BIT(5) + + #define COMMON_SLICE_CHICKEN3 XE_REG(0x7304, XE_REG_OPTION_MASKED) + #define XEHP_COMMON_SLICE_CHICKEN3 XE_REG_MCR(0x7304, XE_REG_OPTION_MASKED) +diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c +index 5766fa7742d31..314cbe70d2f2a 100644 +--- a/drivers/gpu/drm/xe/xe_tuning.c ++++ b/drivers/gpu/drm/xe/xe_tuning.c +@@ -110,6 +110,11 @@ static const struct xe_rtp_entry_sr engine_tunings[] = { + }; + + static const struct xe_rtp_entry_sr lrc_tunings[] = { ++ { XE_RTP_NAME("Tuning: Windower HW Filtering"), ++ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3599), ENGINE_CLASS(RENDER)), ++ XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, HW_FILTERING)) ++ }, ++ + /* DG2 */ + + { XE_RTP_NAME("Tuning: L3 cache"), +-- +2.53.0 + diff --git a/queue-7.0/drm-xe-vf-fix-signature-of-print-functions.patch b/queue-7.0/drm-xe-vf-fix-signature-of-print-functions.patch new file mode 100644 index 0000000000..0afab28e7e --- /dev/null +++ b/queue-7.0/drm-xe-vf-fix-signature-of-print-functions.patch @@ -0,0 +1,124 @@ +From d9a1c6e4e1889267987c14d2a98ff5ff8883a87f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 17:57:26 +0200 +Subject: drm/xe/vf: Fix signature of print functions + +From: Michal Wajdeczko + +[ Upstream commit 9bb2f1d7e6e58b8e434ddc2048c661bf87ccdf2a ] + +We have plugged-in existing VF print functions into our GT debugfs +show helper as-is, but we missed that the helper expects functions +to return int, while they were defined as void. This can lead to +errors being reported when CFI is enabled. + +Fixes: 63d8cb8fe3dd ("drm/xe/vf: Expose SR-IOV VF attributes to GT debugfs") +Signed-off-by: Michal Wajdeczko +Cc: Mohanram Meenakshisundaram +Reviewed-by: Shuicheng Lin +Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com +(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_gt_sriov_vf.c | 24 ++++++++++++++++++------ + drivers/gpu/drm/xe/xe_gt_sriov_vf.h | 6 +++--- + 2 files changed, 21 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +index 30e8c2cf5f09a..82703a25e96d9 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +@@ -1129,13 +1129,15 @@ void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val) + } + + /** +- * xe_gt_sriov_vf_print_config - Print VF self config. ++ * xe_gt_sriov_vf_print_config() - Print VF self config. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config; + struct xe_device *xe = gt_to_xe(gt); +@@ -1162,16 +1164,20 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) + + drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs); + drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF. ++ * xe_gt_sriov_vf_print_runtime() - Print VF's runtime regs received from PF. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + { + struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs; + unsigned int size = gt->sriov.vf.runtime.num_regs; +@@ -1180,16 +1186,20 @@ void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) + + for (; size--; vf_regs++) + drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value); ++ ++ return 0; + } + + /** +- * xe_gt_sriov_vf_print_version - Print VF ABI versions. ++ * xe_gt_sriov_vf_print_version() - Print VF ABI versions. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * This function is for VF use only. ++ * ++ * Return: always 0. + */ +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + { + struct xe_device *xe = gt_to_xe(gt); + struct xe_uc_fw_version *guc_version = >->sriov.vf.guc_version; +@@ -1219,6 +1229,8 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) + GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR); + drm_printf(p, "\thandshake:\t%u.%u\n", + pf_version->major, pf_version->minor); ++ ++ return 0; + } + + static bool vf_post_migration_shutdown(struct xe_gt *gt) +diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +index 7d97189c2d3d9..4f1c7aa422e7b 100644 +--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h ++++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +@@ -35,9 +35,9 @@ bool xe_gt_sriov_vf_sched_groups_enabled(struct xe_gt *gt); + u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg); + void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val); + +-void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); +-void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); ++int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); + + void xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt); + +-- +2.53.0 + diff --git a/queue-7.0/erofs-fix-managed-cache-race-for-unaligned-extents.patch b/queue-7.0/erofs-fix-managed-cache-race-for-unaligned-extents.patch new file mode 100644 index 0000000000..2c6ff4f985 --- /dev/null +++ b/queue-7.0/erofs-fix-managed-cache-race-for-unaligned-extents.patch @@ -0,0 +1,89 @@ +From ce2727e0ed7c0603bedb966dc9b3291144f8c2e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 12:34:31 +0800 +Subject: erofs: fix managed cache race for unaligned extents + +From: Gao Xiang + +[ Upstream commit 649932fc3815eda2f24eb4de4b3a5e94886ee0b9 ] + +After unaligned compressed extents were introduced, the following race +could occur: + +[Thread 1] [Thread 2] +(z_erofs_fill_bio_vec) + +... +filemap_add_folio (1) + (z_erofs_bind_cache) + + .. + .. +folio_attach_private (2) + filemap_add_folio (3) again + +Since (1) is executed but (2) hasn't been executed yet, it's possible +that another thread finds the same managed folio in z_erofs_bind_cache() +for a different pcluster and calls filemap_add_folio() again since +folio->private is still Z_EROFS_PREALLOCATED_FOLIO. + +Fix this by explicitly clearing folio->private before making the folio +visible in the managed cache so that another pcluster can simply wait +on the locked managed folio as what we did for other shared cases [1]. + +This only impacts unaligned data compression (`-E48bit` with zstd, +for example). + +[1] Commit 9e2f9d34dd12 ("erofs: handle overlapped pclusters out of + crafted images properly") was originally introduced to handle crafted + overlapped extents, but it addresses unaligned extents as well. + +Fixes: 7361d1e3763b ("erofs: support unaligned encoded data") +Reported-by: Arseniy Krasnov +Closes: https://lore.kernel.org/r/4a2f3801-fac1-42fe-ae75-da315822e088@salutedevices.com +Tested-by: Arseniy Krasnov +Signed-off-by: Gao Xiang +Signed-off-by: Sasha Levin +--- + fs/erofs/zdata.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c +index fe8121df9ef2f..d7445e98312d8 100644 +--- a/fs/erofs/zdata.c ++++ b/fs/erofs/zdata.c +@@ -1511,8 +1511,15 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, + DBG_BUGON(z_erofs_is_shortlived_page(bvec->bv_page)); + + folio = page_folio(zbv.page); +- /* For preallocated managed folios, add them to page cache here */ ++ /* ++ * Preallocated folios are added to the managed cache here rather than ++ * in z_erofs_bind_cache() in order to keep these folios locked in ++ * increasing (physical) address order. ++ * Clear folio->private before these folios become visible to others in ++ * the managed cache to avoid duplicate additions for unaligned extents. ++ */ + if (folio->private == Z_EROFS_PREALLOCATED_FOLIO) { ++ folio->private = NULL; + tocache = true; + goto out_tocache; + } +@@ -1548,14 +1555,8 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, + } + return; + } +- /* +- * Already linked with another pcluster, which only appears in +- * crafted images by fuzzers for now. But handle this anyway. +- */ +- tocache = false; /* use temporary short-lived pages */ + } else { + DBG_BUGON(1); /* referenced managed folios can't be truncated */ +- tocache = true; + } + folio_unlock(folio); + folio_put(folio); +-- +2.53.0 + diff --git a/queue-7.0/erofs-fix-metabuf-leak-in-inode-xattr-initialization.patch b/queue-7.0/erofs-fix-metabuf-leak-in-inode-xattr-initialization.patch new file mode 100644 index 0000000000..49bafcbb95 --- /dev/null +++ b/queue-7.0/erofs-fix-metabuf-leak-in-inode-xattr-initialization.patch @@ -0,0 +1,66 @@ +From 0a387e85aea2ec10c18a337559c07de479384d41 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 12:46:07 +0800 +Subject: erofs: fix metabuf leak in inode xattr initialization + +From: Jia Zhu + +[ Upstream commit 79b09c54c6563df9846ca3094bcfd72082c3e1d7 ] + +commit bb88e8da0025 ("erofs: use meta buffers for xattr operations") +converted xattr operations to use on-stack erofs_buf instances. +erofs_init_inode_xattrs() uses such a metabuf while reading the inline +xattr header and shared xattr id array. + +Some error paths after erofs_read_metabuf() leave through out_unlock +without dropping the metabuf, so the folio reference can leak. + +Consolidate the cleanup at out_unlock. erofs_put_metabuf() is a +no-op if no folio has been acquired, and this keeps all paths after +taking EROFS_I_BL_XATTR_BIT covered by a single cleanup site. + +Fixes: bb88e8da0025 ("erofs: use meta buffers for xattr operations") +Signed-off-by: Jia Zhu +Reviewed-by: Gao Xiang +Fixes: bb88e8da0025 ("erofs: use meta buffers for xattr operations") +Signed-off-by: Gao Xiang +Signed-off-by: Sasha Levin +--- + fs/erofs/xattr.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c +index 41e311019a251..df7ea019526d7 100644 +--- a/fs/erofs/xattr.c ++++ b/fs/erofs/xattr.c +@@ -89,13 +89,11 @@ static int erofs_init_inode_xattrs(struct inode *inode) + vi->xattr_isize - sizeof(struct erofs_xattr_ibody_header)) { + erofs_err(sb, "invalid h_shared_count %u @ nid %llu", + vi->xattr_shared_count, vi->nid); +- erofs_put_metabuf(&buf); + ret = -EFSCORRUPTED; + goto out_unlock; + } + vi->xattr_shared_xattrs = kmalloc_objs(uint, vi->xattr_shared_count); + if (!vi->xattr_shared_xattrs) { +- erofs_put_metabuf(&buf); + ret = -ENOMEM; + goto out_unlock; + } +@@ -112,12 +110,12 @@ static int erofs_init_inode_xattrs(struct inode *inode) + } + vi->xattr_shared_xattrs[i] = le32_to_cpu(*xattr_id); + } +- erofs_put_metabuf(&buf); + + /* paired with smp_mb() at the beginning of the function. */ + smp_mb(); + set_bit(EROFS_I_EA_INITED_BIT, &vi->flags); + out_unlock: ++ erofs_put_metabuf(&buf); + clear_and_wake_up_bit(EROFS_I_BL_XATTR_BIT, &vi->flags); + return ret; + } +-- +2.53.0 + diff --git a/queue-7.0/erofs-harden-h_shared_count-in-erofs_init_inode_xatt.patch b/queue-7.0/erofs-harden-h_shared_count-in-erofs_init_inode_xatt.patch new file mode 100644 index 0000000000..a4e66772db --- /dev/null +++ b/queue-7.0/erofs-harden-h_shared_count-in-erofs_init_inode_xatt.patch @@ -0,0 +1,52 @@ +From 2a68dc62594fd74066cc1b34c0ace3eb88d95548 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Mar 2026 15:24:39 +0000 +Subject: erofs: harden h_shared_count in erofs_init_inode_xattrs() + +From: Utkal Singh + +[ Upstream commit 6a01f5478d208544c8ba5ddbd674ea660f1b7047 ] + +`u8 h_shared_count` indicates the shared xattr count of an inode. It is +read from the on-disk xattr ibody header, which should be corrupted if +the size of the shared xattr array exceeds the space available in +`xattr_isize`. + +It does not cause harmful consequence (e.g. crashes), since the image is +already considered corrupted, it indeed results in the silent processing +of garbage metadata. + +Let's harden it to report -EFSCORRUPTED earlier. + +Signed-off-by: Utkal Singh +Reviewed-by: Gao Xiang +Reviewed-by: Chao Yu +Signed-off-by: Gao Xiang +Stable-dep-of: 79b09c54c656 ("erofs: fix metabuf leak in inode xattr initialization") +Signed-off-by: Sasha Levin +--- + fs/erofs/xattr.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c +index c411df5d9dfc7..41e311019a251 100644 +--- a/fs/erofs/xattr.c ++++ b/fs/erofs/xattr.c +@@ -85,6 +85,14 @@ static int erofs_init_inode_xattrs(struct inode *inode) + } + vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter); + vi->xattr_shared_count = ih->h_shared_count; ++ if ((u32)vi->xattr_shared_count * sizeof(__le32) > ++ vi->xattr_isize - sizeof(struct erofs_xattr_ibody_header)) { ++ erofs_err(sb, "invalid h_shared_count %u @ nid %llu", ++ vi->xattr_shared_count, vi->nid); ++ erofs_put_metabuf(&buf); ++ ret = -EFSCORRUPTED; ++ goto out_unlock; ++ } + vi->xattr_shared_xattrs = kmalloc_objs(uint, vi->xattr_shared_count); + if (!vi->xattr_shared_xattrs) { + erofs_put_metabuf(&buf); +-- +2.53.0 + diff --git a/queue-7.0/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch b/queue-7.0/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch new file mode 100644 index 0000000000..2eab3c9113 --- /dev/null +++ b/queue-7.0/ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch @@ -0,0 +1,58 @@ +From e8d969a36db3b863d7f82298031ed3c2118d4a08 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:43:43 +0800 +Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics + +From: Chenguang Zhao + +[ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] + +ethnl_bitmap32_not_zero() should return true if some bit in [start, end) +is set: + +- Fix inverted memchr_inv() sense: return true when the scan finds a + non-zero byte, not when the middle words are all zero. +- Return false for an empty interval (end <= start). +- When end is 32-bit aligned, indices in [start, end) do not include any + bits from map[end_word]; return false after earlier checks found no + non-zero data. + +Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") +Signed-off-by: Chenguang Zhao +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ethtool/bitset.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c +index f0883357d12e5..4691d6d0f2b75 100644 +--- a/net/ethtool/bitset.c ++++ b/net/ethtool/bitset.c +@@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + u32 mask; + + if (end <= start) +- return true; ++ return false; + + if (start % 32) { + mask = ethnl_upper_bits(start); +@@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + start_word++; + } + +- if (!memchr_inv(map + start_word, '\0', +- (end_word - start_word) * sizeof(u32))) ++ if (memchr_inv(map + start_word, '\0', ++ (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) +- return true; ++ return false; + return map[end_word] & ethnl_lower_bits(end); + } + +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch b/queue-7.0/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch new file mode 100644 index 0000000000..578fbf288c --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch @@ -0,0 +1,52 @@ +From 24e69e8d2920d40811cea171e2e219a06ed9c655 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:33 +0100 +Subject: firmware: arm_ffa: Align RxTx buffer size before mapping + +From: Sudeep Holla + +[ Upstream commit 0399e3f872ca3d78044bb715a73ea645806d2c7b ] + +Commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during +RXTX_MAP") advertises PAGE_ALIGN(rxtx_bufsz) to firmware when mapping the +buffers but the driver continues to stores the minimum FF-A buffer size +in drv_info->rxtx_bufsz which is used elsewhere in the driver. + +Align the size before storing it so that the allocation, validation and +FFA_RXTX_MAP all use the same buffer size. + +Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") +Cc: Sebastian Ene +Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com +Reviewed-by: Sebastian Ene +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index ed24794986c57..001e6f40881df 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -2106,6 +2106,7 @@ static int __init ffa_init(void) + rxtx_bufsz = SZ_4K; + } + ++ rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); + drv_info->rxtx_bufsz = rxtx_bufsz; + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { +@@ -2121,7 +2122,7 @@ static int __init ffa_init(void) + + ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), + virt_to_phys(drv_info->rx_buffer), +- PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE); ++ rxtx_bufsz / FFA_PAGE_SIZE); + if (ret) { + pr_err("failed to register FFA RxTx buffers\n"); + goto free_pages; +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch b/queue-7.0/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch new file mode 100644 index 0000000000..c6ff04d473 --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-bound-partition_info_get_regs-copie.patch @@ -0,0 +1,100 @@ +From b769c0582ad7bffa513fd39217790730b96f4580 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:30 +0100 +Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies + +From: Sudeep Holla + +[ Upstream commit 3974ea1938406f9bfa7c1f48d4e43533f447bb08 ] + +The register-based PARTITION_INFO_GET path trusted the firmware-provided +indices when copying partition descriptors into the caller buffer. +Reject inconsistent counts or index progressions so the copy loop cannot +write past the allocated array. + +Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-6-8595ae450034@kernel.org +(fixed cur_idx when exactly one descriptor in the first fragment) +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++------ + 1 file changed, 23 insertions(+), 6 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 07c0de772e7d1..e15fda86b6bce 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -323,6 +323,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + #define PART_INFO_ID_MASK GENMASK(15, 0) + #define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) + #define PART_INFO_PROPS_MASK GENMASK(63, 32) ++#define FFA_PART_INFO_GET_REGS_FIRST_REG 3 ++#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 ++#define FFA_PART_INFO_GET_REGS_MAX_DESC \ ++ (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ ++ FFA_PART_INFO_GET_REGS_FIRST_REG) / \ ++ FFA_PART_INFO_GET_REGS_REGS_PER_DESC) + #define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) + #define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) + #define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) +@@ -330,15 +336,13 @@ static int + __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + struct ffa_partition_info *buffer, int num_parts) + { +- u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; ++ u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0; + struct ffa_partition_info *buf = buffer; + ffa_value_t partition_info; + + do { + __le64 *regs; +- int idx; +- +- start_idx = prev_idx ? prev_idx + 1 : 0; ++ int idx, nr_desc, buf_idx; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_PARTITION_INFO_GET_REGS, +@@ -354,15 +358,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + count = PARTITION_COUNT(partition_info.a2); + if (!buffer || !num_parts) /* count only */ + return count; ++ if (count > num_parts) ++ return -EINVAL; + + cur_idx = CURRENT_INDEX(partition_info.a2); ++ if (cur_idx < start_idx || cur_idx >= count) ++ return -EINVAL; ++ ++ nr_desc = cur_idx - start_idx + 1; ++ if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) ++ return -EINVAL; ++ ++ buf_idx = buf - buffer; ++ if (buf_idx + nr_desc > num_parts) ++ return -EINVAL; ++ + tag = UUID_INFO_TAG(partition_info.a2); + buf_sz = PARTITION_INFO_SZ(partition_info.a2); + if (buf_sz > sizeof(*buffer)) + buf_sz = sizeof(*buffer); + + regs = (void *)&partition_info.a3; +- for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) { ++ for (idx = 0; idx < nr_desc; idx++, buf++) { + union { + uuid_t uuid; + u64 regs[2]; +@@ -380,7 +397,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + uuid_copy(&buf->uuid, &uuid_regs.uuid); + regs += 3; + } +- prev_idx = cur_idx; ++ start_idx = cur_idx + 1; + + } while (cur_idx < (count - 1)); + +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch b/queue-7.0/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch new file mode 100644 index 0000000000..eb47155dce --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch @@ -0,0 +1,48 @@ +From efb3ba2d9e71bff8a3489981d749ad08af30bfed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:25 +0100 +Subject: firmware: arm_ffa: Check for NULL FF-A ID table while driver + registration + +From: Sudeep Holla + +[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] + +The bus match callback assumes that every FF-A driver provides an +id_table and dereferences it unconditionally. Enforce that contract at +registration time so a buggy client driver cannot crash the bus during +match. + +Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c +index 9576862d89c40..601c3418e0d92 100644 +--- a/drivers/firmware/arm_ffa/bus.c ++++ b/drivers/firmware/arm_ffa/bus.c +@@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv) + + id_table = to_ffa_driver(drv)->id_table; + ffa_dev = to_ffa_dev(dev); ++ if (!id_table) ++ return 0; + + while (!uuid_is_null(&id_table->uuid)) { + /* +@@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, + { + int ret; + +- if (!driver->probe) ++ if (!driver->probe || !driver->id_table) + return -EINVAL; + + driver->driver.bus = &ffa_bus_type; +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch b/queue-7.0/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch new file mode 100644 index 0000000000..a4ccdaa1c6 --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch @@ -0,0 +1,45 @@ +From bff69d857c5ca4e37c9a7765dd7202c9e463c0d8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:28 +0100 +Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in + workqueue + +From: Sudeep Holla + +[ Upstream commit 9985d5357ed93af0d1933969c247e966957730e1 ] + +Per-vcpu notification handling already runs from a per-cpu work item on +the target cpu. Routing that path back through smp_call_function_single() +re-enters the call-function IPI path and executes the notification +handler with interrupts disabled. That makes the framework path unsafe, +since it takes a mutex, allocates memory with GFP_KERNEL, and invokes +client callbacks. + +Handle per-vcpu self notifications directly from the existing per-cpu +work item instead. This keeps the per-vcpu path in task context and +avoids the extra IPI hop entirely. + +Fixes: 3a3e2b83e805 ("firmware: arm_ffa: Avoid queuing work when running on the worker queue") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-4-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e6a051b20cb72..59d12facb7dd6 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1542,7 +1542,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work) + struct ffa_drv_info *info = container_of(work, struct ffa_drv_info, + notif_pcpu_work); + +- ffa_self_notif_handle(smp_processor_id(), true, info); ++ notif_get_and_handle(info); + } + + static const struct ffa_info_ops ffa_drv_info_ops = { +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch b/queue-7.0/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch new file mode 100644 index 0000000000..0858e2fd6d --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch @@ -0,0 +1,53 @@ +From 844dc4db6a86ec2c0a2b39d8e5681fa8ab0fb4a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:35 +0100 +Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup + +From: Sudeep Holla + +[ Upstream commit a6848a50404eefb6f0b131c21881a2d8d21b31a9 ] + +ffa_sched_recv_cb_update() used list_for_each_entry_safe() to search for +a matching partition and then tested the iterator against NULL. That is +not a valid end-of-list check for circular lists and can fall through +with an invalid pointer. Use a normal iterator and detect the not-found +case correctly before touching the partition state. + +Fixes: be61da938576 ("firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI callback") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-11-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index aa6b9d52a673f..e0263c3fad70b 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1207,7 +1207,7 @@ static int + ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + void *cb_data, bool is_registration) + { +- struct ffa_dev_part_info *partition = NULL, *tmp; ++ struct ffa_dev_part_info *partition = NULL; + struct list_head *phead; + bool cb_valid; + +@@ -1220,11 +1220,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, + return -EINVAL; + } + +- list_for_each_entry_safe(partition, tmp, phead, node) ++ list_for_each_entry(partition, phead, node) + if (partition->dev == dev) + break; + +- if (!partition) { ++ if (&partition->node == phead) { + pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch b/queue-7.0/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch new file mode 100644 index 0000000000..d50a9d377c --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-keep-framework-rx-release-under-loc.patch @@ -0,0 +1,73 @@ +From c93ef346c250bdee74d8aa26312f275e4ac0029e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:31 +0100 +Subject: firmware: arm_ffa: Keep framework RX release under lock + +From: Sudeep Holla + +[ Upstream commit 2af18f8e36b277730527cacc2256b1332f56aa28 ] + +The framework notification handler drops rx_lock before issuing +FFA_RX_RELEASE, leaving a window where another RX-buffer user can +start a new FF-A transaction before ownership has actually been +returned to firmware. + +Move the FFA_RX_RELEASE calls so they execute while rx_lock is still +held on both the kmemdup() failure path and the normal success path. +While doing that, switch the handler to scoped_guard() to keep the +critical section explicit. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-7-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++---------------- + 1 file changed, 13 insertions(+), 16 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e15fda86b6bce..23623b61f2682 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1492,25 +1492,22 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) + return; + +- mutex_lock(&drv_info->rx_lock); ++ scoped_guard(mutex, &drv_info->rx_lock) { ++ msg = drv_info->rx_buffer; ++ buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); ++ if (!buf) { ++ ffa_rx_release(); ++ return; ++ } + +- msg = drv_info->rx_buffer; +- buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); +- if (!buf) { +- mutex_unlock(&drv_info->rx_lock); +- return; ++ target = SENDER_ID(msg->send_recv_id); ++ if (msg->offset >= sizeof(*msg)) ++ uuid_copy(&uuid, &msg->uuid); ++ else ++ uuid_copy(&uuid, &uuid_null); ++ ffa_rx_release(); + } + +- target = SENDER_ID(msg->send_recv_id); +- if (msg->offset >= sizeof(*msg)) +- uuid_copy(&uuid, &msg->uuid); +- else +- uuid_copy(&uuid, &uuid_null); +- +- mutex_unlock(&drv_info->rx_lock); +- +- ffa_rx_release(); +- + read_lock(&drv_info->notify_lock); + cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); + read_unlock(&drv_info->notify_lock); +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch b/queue-7.0/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch new file mode 100644 index 0000000000..61d32da545 --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch @@ -0,0 +1,38 @@ +From 70bb3aaeabb49527f155571bc60f11a9f93e3b69 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:26 +0100 +Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure + +From: Sudeep Holla + +[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] + +If the RX buffer allocation fails in ffa_init(), the error path jumps to +free_pages even though no buffer has been allocated yet. Route that case +directly to free_drv_info so the cleanup path is only used after at +least one RX/TX buffer allocation has succeeded. + +Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index eb27828482837..e6a051b20cb72 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -2067,7 +2067,7 @@ static int __init ffa_init(void) + drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); + if (!drv_info->rx_buffer) { + ret = -ENOMEM; +- goto free_pages; ++ goto free_drv_info; + } + + drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch b/queue-7.0/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch new file mode 100644 index 0000000000..c2e0360d73 --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch @@ -0,0 +1,102 @@ +From 82e434f99d1aab278335ed3452fcbf3093f1282a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:34 +0100 +Subject: firmware: arm_ffa: Snapshot notifier callbacks under lock + +From: Sudeep Holla + +[ Upstream commit 38290b180a4d5746baed796d49f88d56d2f336cd ] + +Both notification handlers currently look up a notifier callback under +notify_lock, drop the lock, and then dereference the returned +notifier entry. A concurrent unregister can delete and free that +entry in the gap, leaving the handler to dereference stale memory. + +Copy the callback pointer and callback data while notify_lock is +still held and invoke the callback only after the lock is dropped. +This keeps the existing callback execution model while removing the +use-after-free window in both the framework and non-framework +notification paths. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-10-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 35 ++++++++++++++++++++----------- + 1 file changed, 23 insertions(+), 12 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 001e6f40881df..aa6b9d52a673f 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1463,20 +1463,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id, + + static void handle_notif_callbacks(u64 bitmap, enum notify_type type) + { ++ ffa_notifier_cb cb; ++ void *cb_data; + int notify_id; +- struct notifier_cb_info *cb_info = NULL; + + for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap; + notify_id++, bitmap >>= 1) { + if (!(bitmap & 1)) + continue; + +- read_lock(&drv_info->notify_lock); +- cb_info = notifier_hnode_get_by_type(notify_id, type); +- read_unlock(&drv_info->notify_lock); ++ scoped_guard(read_lock, &drv_info->notify_lock) { ++ struct notifier_cb_info *cb_info; ++ ++ cb_info = notifier_hnode_get_by_type(notify_id, type); ++ cb = cb_info ? cb_info->cb : NULL; ++ cb_data = cb_info ? cb_info->cb_data : NULL; ++ } + +- if (cb_info && cb_info->cb) +- cb_info->cb(notify_id, cb_info->cb_data); ++ if (cb) ++ cb(notify_id, cb_data); + } + } + +@@ -1484,9 +1489,10 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + { + void *buf; + uuid_t uuid; ++ void *fwk_cb_data; + int notify_id = 0, target; ++ ffa_fwk_notifier_cb fwk_cb; + struct ffa_indirect_msg_hdr *msg; +- struct notifier_cb_info *cb_info = NULL; + size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); + + /* Only one framework notification defined and supported for now */ +@@ -1522,12 +1528,17 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + ffa_rx_release(); + } + +- read_lock(&drv_info->notify_lock); +- cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); +- read_unlock(&drv_info->notify_lock); ++ scoped_guard(read_lock, &drv_info->notify_lock) { ++ struct notifier_cb_info *cb_info; ++ ++ cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, ++ &uuid); ++ fwk_cb = cb_info ? cb_info->fwk_cb : NULL; ++ fwk_cb_data = cb_info ? cb_info->cb_data : NULL; ++ } + +- if (cb_info && cb_info->fwk_cb) +- cb_info->fwk_cb(notify_id, cb_info->cb_data, buf); ++ if (fwk_cb) ++ fwk_cb(notify_id, fwk_cb_data, buf); + kfree(buf); + } + +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch b/queue-7.0/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch new file mode 100644 index 0000000000..2bd4ed302b --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch @@ -0,0 +1,77 @@ +From 016d4b194c1e4dd4442aeb7f79b7c2088b89993e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:29 +0100 +Subject: firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0 + +From: Sudeep Holla + +[ Upstream commit 6d3daa9b8d313f42d52e75590310f26a29b61b44 ] + +For FF-A v1.0 the driver registers a bus notifier to backfill UUID +matching, but the notifier was never unregistered on cleanup paths. +Track the registration state and unregister it during teardown and early +partition-setup failure. + +Fixes: 9dd15934f60d ("firmware: arm_ffa: Move the FF-A v1.0 NULL UUID workaround to bus notifier") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-5-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 59d12facb7dd6..07c0de772e7d1 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -100,6 +100,7 @@ struct ffa_drv_info { + bool mem_ops_native; + bool msg_direct_req2_supp; + bool bitmap_created; ++ bool bus_notifier_registered; + bool notif_enabled; + unsigned int sched_recv_irq; + unsigned int notif_pend_irq; +@@ -1629,6 +1630,15 @@ static struct notifier_block ffa_bus_nb = { + .notifier_call = ffa_bus_notifier, + }; + ++static void ffa_bus_notifier_unregister(void) ++{ ++ if (!drv_info->bus_notifier_registered) ++ return; ++ ++ bus_unregister_notifier(&ffa_bus_type, &ffa_bus_nb); ++ drv_info->bus_notifier_registered = false; ++} ++ + static int ffa_xa_add_partition_info(struct ffa_device *dev) + { + struct ffa_dev_part_info *info; +@@ -1712,6 +1722,8 @@ static void ffa_partitions_cleanup(void) + struct list_head *phead; + unsigned long idx; + ++ ffa_bus_notifier_unregister(); ++ + /* Clean up/free all registered devices */ + ffa_devices_unregister(); + +@@ -1739,11 +1751,14 @@ static int ffa_setup_partitions(void) + ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb); + if (ret) + pr_err("Failed to register FF-A bus notifiers\n"); ++ else ++ drv_info->bus_notifier_registered = true; + } + + count = ffa_partition_probe(&uuid_null, &pbuf); + if (count <= 0) { + pr_info("%s: No partitions found, error %d\n", __func__, count); ++ ffa_bus_notifier_unregister(); + return -EINVAL; + } + +-- +2.53.0 + diff --git a/queue-7.0/firmware-arm_ffa-validate-framework-notification-mes.patch b/queue-7.0/firmware-arm_ffa-validate-framework-notification-mes.patch new file mode 100644 index 0000000000..e66919d6fb --- /dev/null +++ b/queue-7.0/firmware-arm_ffa-validate-framework-notification-mes.patch @@ -0,0 +1,71 @@ +From ad39e7a3c5456651c31a7d7c29caff36557da3c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 19:33:32 +0100 +Subject: firmware: arm_ffa: Validate framework notification message layout + +From: Sudeep Holla + +[ Upstream commit 4a1cc9e96b311d2609a6f963a5e35bd4ae730d97 ] + +Framework notifications carry an indirect message in the shared RX +buffer. Validate the reported offset and size before using them, reject +zero-length payloads, and ensure that any non-header payload starts at +the UUID field rather than in the middle of the message header. + +Use the validated offset and size values for both kmemdup() and the UUID +parsing path so malformed firmware data cannot drive an out-of-bounds +read or an oversized allocation. + +Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") +Link: https://patch.msgid.link/20260428-ffa_fixes-v2-8-8595ae450034@kernel.org +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index 23623b61f2682..ed24794986c57 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -1487,21 +1487,35 @@ static void handle_fwk_notif_callbacks(u32 bitmap) + int notify_id = 0, target; + struct ffa_indirect_msg_hdr *msg; + struct notifier_cb_info *cb_info = NULL; ++ size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); + + /* Only one framework notification defined and supported for now */ + if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) + return; + + scoped_guard(mutex, &drv_info->rx_lock) { ++ u32 offset, size; ++ + msg = drv_info->rx_buffer; +- buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); ++ offset = msg->offset; ++ size = msg->size; ++ ++ if (!size || (offset != min_offset && offset < sizeof(*msg)) || ++ offset > drv_info->rxtx_bufsz || ++ size > drv_info->rxtx_bufsz - offset) { ++ pr_err("invalid framework notification message\n"); ++ ffa_rx_release(); ++ return; ++ } ++ ++ buf = kmemdup((void *)msg + offset, size, GFP_KERNEL); + if (!buf) { + ffa_rx_release(); + return; + } + + target = SENDER_ID(msg->send_recv_id); +- if (msg->offset >= sizeof(*msg)) ++ if (offset >= sizeof(*msg)) + uuid_copy(&uuid, &msg->uuid); + else + uuid_copy(&uuid, &uuid_null); +-- +2.53.0 + diff --git a/queue-7.0/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch b/queue-7.0/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch new file mode 100644 index 0000000000..f93460f458 --- /dev/null +++ b/queue-7.0/fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch @@ -0,0 +1,124 @@ +From b3984f777571b630f7e7adb4e4f518076ac4d8f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 16:46:29 +0900 +Subject: fprobe: Fix unregister_fprobe() to wait for RCU grace period + +From: Masami Hiramatsu (Google) + +[ Upstream commit 657b594b2084b39a4bc6d8493aa2140cb00cea49 ] + +Commit 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") +changed fprobe to register struct fprobe to an rcu-hlist, but it forgot +to wait for RCU GP. Thus there can be use-after-free if the fprobe is +released right after unregistering. This can be happened on fprobe +event and sample module code. + +To fix this issue, add synchronize_rcu() in unregister_fprobe(). + +Note that BPF is OK because fprobe is used as a part of +bpf_kprobe_multi_link. This unregisters its fprobe in +bpf_kprobe_multi_link_release() and it is deallocated via +bpf_kprobe_multi_link_dealloc(), which is invoked from +bpf_link_defer_dealloc_rcu_gp() RCU callback. + +For BPF, this also introduced unregister_fprobe_async() which does +NOT wait for RCU grace priod. + +Link: https://lore.kernel.org/all/177813998919.256460.2809243930741138224.stgit@mhiramat.tok.corp.google.com/ + +Fixes: 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/linux/fprobe.h | 5 +++++ + kernel/trace/bpf_trace.c | 3 ++- + kernel/trace/fprobe.c | 23 +++++++++++++++++++++-- + 3 files changed, 28 insertions(+), 3 deletions(-) + +diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h +index 0a3bcd1718f37..be1b38c981d4d 100644 +--- a/include/linux/fprobe.h ++++ b/include/linux/fprobe.h +@@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter + int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num); + int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); + int unregister_fprobe(struct fprobe *fp); ++int unregister_fprobe_async(struct fprobe *fp); + bool fprobe_is_registered(struct fprobe *fp); + int fprobe_count_ips_from_filter(const char *filter, const char *notfilter); + #else +@@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp) + { + return -EOPNOTSUPP; + } ++static inline int unregister_fprobe_async(struct fprobe *fp) ++{ ++ return -EOPNOTSUPP; ++} + static inline bool fprobe_is_registered(struct fprobe *fp) + { + return false; +diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c +index af7079aa0f36d..a02bd258677ee 100644 +--- a/kernel/trace/bpf_trace.c ++++ b/kernel/trace/bpf_trace.c +@@ -2384,7 +2384,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) + struct bpf_kprobe_multi_link *kmulti_link; + + kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); +- unregister_fprobe(&kmulti_link->fp); ++ /* Don't wait for RCU GP here. */ ++ unregister_fprobe_async(&kmulti_link->fp); + kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt); + } + +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 0afaae4e1a59c..fe4d630aa4460 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -1001,14 +1001,15 @@ static int unregister_fprobe_nolock(struct fprobe *fp) + } + + /** +- * unregister_fprobe() - Unregister fprobe. ++ * unregister_fprobe_async() - Unregister fprobe without RCU GP wait + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). ++ * This function will NOT wait until the fprobe is no longer used. + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +-int unregister_fprobe(struct fprobe *fp) ++int unregister_fprobe_async(struct fprobe *fp) + { + guard(mutex)(&fprobe_mutex); + if (!fp || !fprobe_registered(fp)) +@@ -1016,6 +1017,24 @@ int unregister_fprobe(struct fprobe *fp) + + return unregister_fprobe_nolock(fp); + } ++ ++/** ++ * unregister_fprobe() - Unregister fprobe with RCU GP wait ++ * @fp: A fprobe data structure to be unregistered. ++ * ++ * Unregister fprobe (and remove ftrace hooks from the function entries). ++ * This function will block until the fprobe is no longer used. ++ * ++ * Return 0 if @fp is unregistered successfully, -errno if not. ++ */ ++int unregister_fprobe(struct fprobe *fp) ++{ ++ int ret = unregister_fprobe_async(fp); ++ ++ if (!ret) ++ synchronize_rcu(); ++ return ret; ++} + EXPORT_SYMBOL_GPL(unregister_fprobe); + + static int __init fprobe_initcall(void) +-- +2.53.0 + diff --git a/queue-7.0/fs-fix-forced-iversion-increment-on-lazytime-timesta.patch b/queue-7.0/fs-fix-forced-iversion-increment-on-lazytime-timesta.patch new file mode 100644 index 0000000000..932f598d93 --- /dev/null +++ b/queue-7.0/fs-fix-forced-iversion-increment-on-lazytime-timesta.patch @@ -0,0 +1,62 @@ +From 5ea51abd81fd9da939fd3d86c2efc689f4fe10c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 13:19:18 +0200 +Subject: fs: fix forced iversion increment on lazytime timestamp updates + +From: Pankaj Raghav + +[ Upstream commit 834e98acb748025c04fed3cac9c8954454f4b520 ] + +When updating timestamps with lazytime enabled, if only I_DIRTY_TIME is +set (pure lazytime update), inode_maybe_inc_iversion() should not be +forced to increment i_version. The force parameter should only be true +when actual data or metadata changes require an iversion bump. + +The current code uses "!!dirty" which evaluates to true whenever dirty +has any bits set, including the I_DIRTY_TIME bit alone. This forces an +iversion increment on every lazytime timestamp update, which then sets +I_DIRTY_SYNC, triggering expensive log flushes on subsequent fdatasync +calls. Andres reported this issue when he noticed a perf regression[1]. + +Fix this by using "dirty != I_DIRTY_TIME" as the force parameter. This +passes false for pure lazytime updates (allowing the I_VERSION_QUERIED +optimization to work), while still forcing the increment when dirty +contains other flags indicating real changes that require iversion +updates. + +[1] https://lore.kernel.org/linux-xfs/7ys6erh3nnyeerv2nybyfvp7dmaknuxrlxv74wx56ocdothkc6@ekfiadtkfn2r/ + +Fixes: 85c871a02b03 ("fs: add support for non-blocking timestamp updates") +Signed-off-by: Pankaj Raghav +Link: https://patch.msgid.link/20260511111918.1793689-1-p.raghav@samsung.com +Reviewed-by: Jeff Layton +Reviewed-by: Christoph Hellwig +Reviewed-by: Carlos Maiolino +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/inode.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/fs/inode.c b/fs/inode.c +index cc12b68e021b2..e10439d8d7d98 100644 +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -2130,7 +2130,13 @@ static int inode_update_cmtime(struct inode *inode, unsigned int flags) + inode_iversion_need_inc(inode)) + return -EAGAIN; + } else { +- if (inode_maybe_inc_iversion(inode, !!dirty)) ++ /* ++ * Don't force iversion increment for pure lazytime ++ * updates (I_DIRTY_TIME only), let I_VERSION_QUERIED ++ * dictate whether the increment is needed. ++ */ ++ if (inode_maybe_inc_iversion(inode, ++ dirty != I_DIRTY_TIME)) + dirty |= I_DIRTY_SYNC; + } + } +-- +2.53.0 + diff --git a/queue-7.0/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch b/queue-7.0/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch new file mode 100644 index 0000000000..b1c74860f4 --- /dev/null +++ b/queue-7.0/fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch @@ -0,0 +1,55 @@ +From 7e6951bb185358d0458d3b037989ccc9beabc97b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 1 May 2026 15:10:58 +0800 +Subject: fs: Fix return in jfs_mkdir and orangefs_mkdir + +From: Hongling Zeng + +[ Upstream commit a7cf1da7ac016490d6a1106f2aa6b602d34e9a12 ] + +Return NULL instead of passing to ERR_PTR while err is zero +Fixes these smatch warnings: + - fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR' + - fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero + to 'ERR_PTR' + +Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") +Signed-off-by: Hongling Zeng +Link: https://patch.msgid.link/20260501071058.1243245-1-zenghongling@kylinos.cn +Reviewed-by: Jori Koolstra +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/jfs/namei.c | 2 +- + fs/orangefs/namei.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c +index 60c4a0e0fca5e..442d626792622 100644 +--- a/fs/jfs/namei.c ++++ b/fs/jfs/namei.c +@@ -309,7 +309,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, + out1: + + jfs_info("jfs_mkdir: rc:%d", rc); +- return ERR_PTR(rc); ++ return rc ? ERR_PTR(rc) : NULL; + } + + /* +diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c +index bec5475de094d..75e65e72c2d64 100644 +--- a/fs/orangefs/namei.c ++++ b/fs/orangefs/namei.c +@@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + __orangefs_setattr(dir, &iattr); + out: + op_release(new_op); +- return ERR_PTR(ret); ++ return ret ? ERR_PTR(ret) : NULL; + } + + static int orangefs_rename(struct mnt_idmap *idmap, +-- +2.53.0 + diff --git a/queue-7.0/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch b/queue-7.0/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch new file mode 100644 index 0000000000..92d12537af --- /dev/null +++ b/queue-7.0/fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch @@ -0,0 +1,49 @@ +From 36486ccd42b001b6e76ca4696a3e2a71b3486e9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 4 May 2026 20:26:49 +0900 +Subject: fs/statmount: fix slab out-of-bounds write in statmount_mnt_idmap + +From: Junyoung Jang + +[ Upstream commit a3bf0f28d4ba16e1f35f8c983bb04426b87e2a78 ] + +statmount_mnt_idmap() writes one mapping with seq_printf() and then +manually advances seq->count to include the NUL separator. + +If seq_printf() overflows, seq_set_overflow() sets seq->count to +seq->size. The manual seq->count++ changes this to seq->size + 1. +seq_has_overflowed() then no longer detects the overflow. The corrupted +count returns to statmount_string(), which later executes: + + seq->buf[seq->count++] = '\0'; + +This causes a 1-byte NULL out-of-bounds write on the dynamically +allocated seq buffer. + +Fix this by checking for overflow immediately after seq_printf(). + +Fixes: 37c4a9590e1e ("statmount: allow to retrieve idmappings") +Signed-off-by: Junyoung Jang +Link: https://patch.msgid.link/20260504112649.1862936-1-graypanda.inzag@gmail.com +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/mnt_idmapping.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/mnt_idmapping.c b/fs/mnt_idmapping.c +index 6472c4ea3d1e6..cb61fbdb52e90 100644 +--- a/fs/mnt_idmapping.c ++++ b/fs/mnt_idmapping.c +@@ -375,6 +375,8 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_ + continue; + + seq_printf(seq, "%u %u %u", extent->first, lower, extent->count); ++ if (seq_has_overflowed(seq)) ++ return -EAGAIN; + + seq->count++; /* mappings are separated by \0 */ + if (seq_has_overflowed(seq)) +-- +2.53.0 + diff --git a/queue-7.0/gcc-plugins-always-define-const_cast_gimple-and-cons.patch b/queue-7.0/gcc-plugins-always-define-const_cast_gimple-and-cons.patch new file mode 100644 index 0000000000..1306e02217 --- /dev/null +++ b/queue-7.0/gcc-plugins-always-define-const_cast_gimple-and-cons.patch @@ -0,0 +1,45 @@ +From e54799d8fed989231894d10c78e6e40a3714c8c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 14 Mar 2026 14:24:56 +0100 +Subject: gcc-plugins: Always define CONST_CAST_GIMPLE and CONST_CAST_TREE + +From: Kees Cook + +[ Upstream commit 905c559e51497b8bfdbb68df8be56d2f70f0de8e ] + +For gcc-16, the CONST_CAST macro family was removed. Add back what +we were using in gcc-common.h, as they are simple wrappers. + +See GCC commits: + c3d96ff9e916c02584aa081f03ab999292efbb50 + 458c7926d48959abcb2c1adaa22458e27459a551 + +Suggested-by: Ingo Saitz +Link: https://lore.kernel.org/lkml/ab6OKoay0OWkywjK@spatz.zoo +Fixes: 6b90bd4ba40b ("GCC plugin infrastructure") +Tested-by: Ivan Bulatovic +Tested-by: Christopher Cradock +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + scripts/gcc-plugins/gcc-common.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h +index 8f1b3500f8e2d..abb1964c44d4e 100644 +--- a/scripts/gcc-plugins/gcc-common.h ++++ b/scripts/gcc-plugins/gcc-common.h +@@ -309,7 +309,9 @@ typedef const gimple *const_gimple_ptr; + #define gimple gimple_ptr + #define const_gimple const_gimple_ptr + #undef CONST_CAST_GIMPLE +-#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) ++#define CONST_CAST_GIMPLE(X) const_cast((X)) ++#undef CONST_CAST_TREE ++#define CONST_CAST_TREE(X) const_cast((X)) + + /* gimple related */ + static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +-- +2.53.0 + diff --git a/queue-7.0/gpio-aggregator-fix-a-potential-use-after-free.patch b/queue-7.0/gpio-aggregator-fix-a-potential-use-after-free.patch new file mode 100644 index 0000000000..f76b31542a --- /dev/null +++ b/queue-7.0/gpio-aggregator-fix-a-potential-use-after-free.patch @@ -0,0 +1,41 @@ +From 9cc1563a9107470c4874297d97f9d3c6a14a859d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 10:49:11 +0200 +Subject: gpio: aggregator: fix a potential use-after-free + +From: Bartosz Golaszewski + +[ Upstream commit 30c073cab97afb31901f94de9605177b6b84367e ] + +On error we free aggr->lookups->dev_id before removing the entry from +the lookup table. If a concurrent thread calls gpiod_find() before we +remove the entry, it could iterate over the list and call +gpiod_match_lookup_table() which unconditionally dereferences dev_id +when calling strcmp(). Reverse the order of cleanup. + +Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260520084911.27938-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index 9adf3228c12a8..6c84ca3ff1b64 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -969,8 +969,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + return 0; + + err_remove_lookup_table: +- kfree(aggr->lookups->dev_id); + gpiod_remove_lookup_table(aggr->lookups); ++ kfree(aggr->lookups->dev_id); + err_remove_swnode: + fwnode_remove_software_node(swnode); + err_remove_lookups: +-- +2.53.0 + diff --git a/queue-7.0/gpio-aggregator-lock-device-when-calling-device_is_b.patch b/queue-7.0/gpio-aggregator-lock-device-when-calling-device_is_b.patch new file mode 100644 index 0000000000..8c837a8b80 --- /dev/null +++ b/queue-7.0/gpio-aggregator-lock-device-when-calling-device_is_b.patch @@ -0,0 +1,43 @@ +From f09399a24b715fd327b456966c29efe9ab68ac7d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 11:53:18 +0200 +Subject: gpio: aggregator: lock device when calling device_is_bound() + +From: Bartosz Golaszewski + +[ Upstream commit 598a2b3e2e0e6aa2e9f7843c96c45b5ea11e0411 ] + +The kerneldoc for device_is_bound() says it must be called with the +device lock taken. Add missing synchronization to this driver. + +Fixes: 3a27f40b4570 ("gpio: aggregator: stop using dev-sync-probe") +Link: https://patch.msgid.link/20260518-gpio-dev-lock-v1-2-cc4736f3ff0b@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index a9ad809708fb6..bc6699a821ee7 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -968,9 +968,12 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + } + + wait_for_device_probe(); +- if (!device_is_bound(&pdev->dev)) { +- ret = -ENXIO; +- goto err_unregister_pdev; ++ ++ scoped_guard(device, &pdev->dev) { ++ if (!device_is_bound(&pdev->dev)) { ++ ret = -ENXIO; ++ goto err_unregister_pdev; ++ } + } + + aggr->pdev = pdev; +-- +2.53.0 + diff --git a/queue-7.0/gpio-aggregator-remove-the-software-node-when-deacti.patch b/queue-7.0/gpio-aggregator-remove-the-software-node-when-deacti.patch new file mode 100644 index 0000000000..f8e0067c79 --- /dev/null +++ b/queue-7.0/gpio-aggregator-remove-the-software-node-when-deacti.patch @@ -0,0 +1,47 @@ +From 6ea3583c02a18fbeff634c32beb86eeadb8660cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:16:31 +0200 +Subject: gpio: aggregator: remove the software node when deactivating the + aggregator + +From: Bartosz Golaszewski + +[ Upstream commit 61fef83f239ecace1cce716135762a2d9b7b1fc6 ] + +The dynamic software node we create for the aggregator platform device +when using configfs is leaked when the device is deactivated. Destroy it +as the last step in the tear-down path. + +Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") +Reported-by: Geert Uytterhoeven +Closes: https://lore.kernel.org/all/CAMuHMdVZ=XUvJTGdDAjnkxgtw7Uvnn61iOy3XN_5XNZM2anctw@mail.gmail.com/ +Link: https://patch.msgid.link/20260520121631.33976-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-aggregator.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index b53230065f50e..a9ad809708fb6 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -991,11 +991,15 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) + { ++ struct fwnode_handle *swnode; ++ ++ swnode = dev_fwnode(&aggr->pdev->dev); + platform_device_unregister(aggr->pdev); + aggr->pdev = NULL; + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); ++ fwnode_remove_software_node(swnode); + } + + static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, +-- +2.53.0 + diff --git a/queue-7.0/gpio-aggregator-stop-using-dev-sync-probe.patch b/queue-7.0/gpio-aggregator-stop-using-dev-sync-probe.patch new file mode 100644 index 0000000000..d1c5e03559 --- /dev/null +++ b/queue-7.0/gpio-aggregator-stop-using-dev-sync-probe.patch @@ -0,0 +1,161 @@ +From 26295d0597ba58d958ea3961d636edebf1b86f8a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Mar 2026 11:31:12 +0100 +Subject: gpio: aggregator: stop using dev-sync-probe + +From: Bartosz Golaszewski + +[ Upstream commit 3a27f40b457053e6112a63d14590e4a3ff553b44 ] + +dev-err-probe is an overengineered solution to a simple problem. Use a +combination of wait_for_probe() and device_is_bound() to synchronously +wait for the platform device to probe. + +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260327-gpio-kill-dev-sync-probe-v1-2-efac254f1a1d@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 61fef83f239e ("gpio: aggregator: remove the software node when deactivating the aggregator") +Signed-off-by: Sasha Levin +--- + drivers/gpio/Kconfig | 1 - + drivers/gpio/gpio-aggregator.c | 38 +++++++++++++++++++--------------- + 2 files changed, 21 insertions(+), 18 deletions(-) + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index b45fb799e36c1..e63096002e92d 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1986,7 +1986,6 @@ menu "Virtual GPIO drivers" + config GPIO_AGGREGATOR + tristate "GPIO Aggregator" + select CONFIGFS_FS +- select DEV_SYNC_PROBE + help + Say yes here to enable the GPIO Aggregator, which provides a way to + aggregate existing GPIO lines into a new virtual GPIO chip. +diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c +index 6c84ca3ff1b64..b53230065f50e 100644 +--- a/drivers/gpio/gpio-aggregator.c ++++ b/drivers/gpio/gpio-aggregator.c +@@ -32,8 +32,6 @@ + #include + #include + +-#include "dev-sync-probe.h" +- + #define AGGREGATOR_MAX_GPIOS 512 + #define AGGREGATOR_LEGACY_PREFIX "_sysfs" + +@@ -42,7 +40,7 @@ + */ + + struct gpio_aggregator { +- struct dev_sync_probe_data probe_data; ++ struct platform_device *pdev; + struct config_group group; + struct gpiod_lookup_table *lookups; + struct mutex lock; +@@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) + { + lockdep_assert_held(&aggr->lock); + +- return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); ++ return aggr->pdev && platform_get_drvdata(aggr->pdev); + } + + /* Only aggregators created via legacy sysfs can be "activating". */ +@@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) + { + lockdep_assert_held(&aggr->lock); + +- return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); ++ return aggr->pdev && !platform_get_drvdata(aggr->pdev); + } + + static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) +@@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + { + struct platform_device_info pdevinfo; + struct gpio_aggregator_line *line; ++ struct platform_device *pdev; + struct fwnode_handle *swnode; + unsigned int n = 0; + int ret = 0; +@@ -962,12 +961,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + gpiod_add_lookup_table(aggr->lookups); + +- ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); +- if (ret) ++ pdev = platform_device_register_full(&pdevinfo); ++ if (IS_ERR(pdev)) { ++ ret = PTR_ERR(pdev); + goto err_remove_lookup_table; ++ } + ++ wait_for_device_probe(); ++ if (!device_is_bound(&pdev->dev)) { ++ ret = -ENXIO; ++ goto err_unregister_pdev; ++ } ++ ++ aggr->pdev = pdev; + return 0; + ++err_unregister_pdev: ++ platform_device_unregister(pdev); + err_remove_lookup_table: + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); +@@ -981,7 +991,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) + + static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) + { +- dev_sync_probe_unregister(&aggr->probe_data); ++ platform_device_unregister(aggr->pdev); ++ aggr->pdev = NULL; + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); +@@ -1145,7 +1156,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) + + guard(mutex)(&aggr->lock); + +- pdev = aggr->probe_data.pdev; ++ pdev = aggr->pdev; + if (pdev) + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + +@@ -1322,7 +1333,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); +- dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; + } +@@ -1471,12 +1481,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, + scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + +- /* +- * Since the device created by sysfs might be toggled via configfs +- * 'live' attribute later, this initialization is needed. +- */ +- dev_sync_probe_init(&aggr->probe_data); +- + /* Expose to configfs */ + res = configfs_register_group(&gpio_aggregator_subsys.su_group, + &aggr->group); +@@ -1495,7 +1499,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, + goto remove_table; + } + +- aggr->probe_data.pdev = pdev; ++ aggr->pdev = pdev; + module_put(THIS_MODULE); + return count; + +-- +2.53.0 + diff --git a/queue-7.0/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch b/queue-7.0/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch new file mode 100644 index 0000000000..8ef1759bf7 --- /dev/null +++ b/queue-7.0/gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch @@ -0,0 +1,59 @@ +From d18970fcf380d7932a05ffada67f1f3ca20f52f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:42:16 +0200 +Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed + +From: Bartosz Golaszewski + +[ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] + +We check the padding of other uAPI v2 structures but not that of line +config attributes. For used attributes: check if their padding is +zeroed, for unused: check if the entire structure is zeroed. + +Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") +Reviewed-by: Kent Gibson +Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 73ae77f0f2133..78df6d517d307 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -1184,6 +1184,7 @@ static int gpio_v2_line_flags_validate(u64 flags) + static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + unsigned int num_lines) + { ++ size_t unused_attrs; + unsigned int i; + u64 flags; + int ret; +@@ -1191,9 +1192,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, + if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) + return -EINVAL; + ++ unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; ++ + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) + return -EINVAL; + ++ for (i = 0; i < lc->num_attrs; i++) { ++ if (lc->attrs[i].attr.padding != 0) ++ return -EINVAL; ++ } ++ ++ if (unused_attrs) { ++ if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) ++ return -EINVAL; ++ } ++ + for (i = 0; i < num_lines; i++) { + flags = gpio_v2_line_config_flags(lc, i); + ret = gpio_v2_line_flags_validate(flags); +-- +2.53.0 + diff --git a/queue-7.0/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch b/queue-7.0/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch new file mode 100644 index 0000000000..89629bd743 --- /dev/null +++ b/queue-7.0/hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch @@ -0,0 +1,47 @@ +From a9d64c72fe7eb783f45f265637b6ff5e46eaf195 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Apr 2026 10:10:02 +0300 +Subject: HID: intel-thc-hid: Intel-quickspi: Fix some error codes + +From: Dan Carpenter + +[ Upstream commit ae4ac077332ea3341a0f4c0973556c6b7ac5b7a1 ] + +If we have a partial read that is supposed to be treated as failure but +in this code we forgot to set the error code. Return -EINVAL. + +Fixes: 9d8d51735a3a ("HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation") +Signed-off-by: Dan Carpenter +Reviewed-by: Even Xu +Reviewed-by: Mark Pearson +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +index 16f780bc879b1..cb19057f1191b 100644 +--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c ++++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +@@ -94,7 +94,7 @@ static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) + dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", + input_len, read_len); +- return ret; ++ return ret ?: -EINVAL; + } + + input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; +@@ -318,7 +318,7 @@ int reset_tic(struct quickspi_device *qsdev) + dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", + read_len, actual_read_len); +- return ret; ++ return ret ?: -EINVAL; + } + + input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); +-- +2.53.0 + diff --git a/queue-7.0/hid-quirks-really-enable-the-intended-work-around-fo.patch b/queue-7.0/hid-quirks-really-enable-the-intended-work-around-fo.patch new file mode 100644 index 0000000000..cf0bacdc0d --- /dev/null +++ b/queue-7.0/hid-quirks-really-enable-the-intended-work-around-fo.patch @@ -0,0 +1,42 @@ +From 7fc4071250a1a86315dfc4192d37f9f83a7a451e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Feb 2026 09:11:31 +0100 +Subject: HID: quirks: really enable the intended work around for appledisplay + +From: Lukas Bulwahn + +[ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] + +Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for +appledisplay") intends to add a quirk for kernels built with Apple Cinema +Display support, but it refers to the non-existing config option +CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display +support is named CONFIG_USB_APPLEDISPLAY. + +Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef +directive. + +Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") +Signed-off-by: Lukas Bulwahn +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-quirks.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 02f7db5c10564..5e754b0a50325 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -234,7 +234,7 @@ static const struct hid_device_id hid_quirks[] = { + * used as a driver. See hid_scan_report(). + */ + static const struct hid_device_id hid_have_special_driver[] = { +-#if IS_ENABLED(CONFIG_APPLEDISPLAY) ++#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, +-- +2.53.0 + diff --git a/queue-7.0/hid-uclogic-fix-regression-of-input-name-assignment.patch b/queue-7.0/hid-uclogic-fix-regression-of-input-name-assignment.patch new file mode 100644 index 0000000000..11b1583e8e --- /dev/null +++ b/queue-7.0/hid-uclogic-fix-regression-of-input-name-assignment.patch @@ -0,0 +1,44 @@ +From 0c9ecca55989abb66652914ca51e253b3f5485d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 10:33:16 +0200 +Subject: HID: uclogic: Fix regression of input name assignment + +From: Takashi Iwai + +[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] + +The previous fix for adding the devm_kasprintf() return check in the +commit bd07f751208b ("HID: uclogic: Add NULL check in +uclogic_input_configured()") changed the condition of hi->input->name +assignment, and it resulted in missing the proper input device name +when no custom suffix is defined. + +Restore the conditional to the original content to address the +regression. + +Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") +Signed-off-by: Takashi Iwai +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-uclogic-core.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c +index bd7f93e96e4e4..b73f09d26688a 100644 +--- a/drivers/hid/hid-uclogic-core.c ++++ b/drivers/hid/hid-uclogic-core.c +@@ -184,7 +184,9 @@ static int uclogic_input_configured(struct hid_device *hdev, + suffix = "System Control"; + break; + } +- } else { ++ } ++ ++ if (suffix) { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) +-- +2.53.0 + diff --git a/queue-7.0/hwmon-lm90-add-lock-protection-to-lm90_alert.patch b/queue-7.0/hwmon-lm90-add-lock-protection-to-lm90_alert.patch new file mode 100644 index 0000000000..171178fac2 --- /dev/null +++ b/queue-7.0/hwmon-lm90-add-lock-protection-to-lm90_alert.patch @@ -0,0 +1,58 @@ +From 3aa0500c25815a0d3c946812e92dd0cfc8c5ef75 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 14:41:00 -0700 +Subject: hwmon: (lm90) Add lock protection to lm90_alert + +From: Guenter Roeck + +[ Upstream commit 873e919e3101063a7a75989510ccfc125a4391cf ] + +Sashiko reports: + +lm90_alert() executes in the smbus alert context and calls +lm90_update_confreg() to disable the hardware alert line, without +acquiring hwmon_lock. + +Concurrently, sysfs write operations (such as lm90_write_convrate) hold +the hwmon_lock, temporarily modify data->config, and then restore it. + +If an alert interrupt occurs concurrently with a sysfs write, the sysfs +path will overwrite the alert handler's modifications to data->config +and the hardware register. + +This unintentionally re-enables the hardware alert line while the alarm is +still active, causing an interrupt storm. + +Add the missing lock to lm90_alert() to solve the problem. + +Fixes: 7a1d220ccb0cc ("hwmon: (lm90) Introduce function to update configuration register") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index c4a9dafff81d6..1eeb608e59039 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -2946,6 +2946,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + */ + struct lm90_data *data = i2c_get_clientdata(client); + ++ hwmon_lock(data->hwmon_dev); + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { +@@ -2955,6 +2956,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); + } ++ hwmon_unlock(data->hwmon_dev); + } else { + dev_dbg(&client->dev, "Everything OK\n"); + } +-- +2.53.0 + diff --git a/queue-7.0/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch b/queue-7.0/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch new file mode 100644 index 0000000000..30d59dbb0e --- /dev/null +++ b/queue-7.0/hwmon-lm90-stop-work-before-releasing-hwmon-device.patch @@ -0,0 +1,108 @@ +From 7971f3e0a9a49409764dfd7ec11dd11c866f73e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 14:31:49 -0700 +Subject: hwmon: (lm90) Stop work before releasing hwmon device + +From: Guenter Roeck + +[ Upstream commit b09a45601094c7f4ec4db8090b825fa61e169d93 ] + +Sashiko reports: + +In lm90_probe(), the devm action to cancel the alert_work and report_work +(lm90_restore_conf) is registered in lm90_init_client() before +devm_hwmon_device_register_with_info() is called. + +Because devm executes cleanup actions in reverse order during module +unbind or probe failure, the hwmon device is unregistered and freed first. + +If lm90_alert_work() or lm90_report_alarms() runs in the window between +the hwmon device being freed and the delayed works being cancelled, +lm90_update_alarms() will dereference the freed data->hwmon_dev here. + +Fix the problem by canceling the workers separately after registering +the hwmon device and before registering the interrupt handler. This ensures +that the workers are canceled after interrupts are disabled and before +the hwmon device is released. Add "shutdown" flag to indicate that device +shutdown is in progress to prevent workers from being re-armed. + +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index 3c10a5066b53d..c4a9dafff81d6 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -736,6 +736,7 @@ struct lm90_data { + struct hwmon_chip_info chip; + struct delayed_work alert_work; + struct work_struct report_work; ++ bool shutdown; /* true if shutting down */ + bool valid; /* true if register values are valid */ + bool alarms_valid; /* true if status register values are valid */ + unsigned long last_updated; /* in jiffies */ +@@ -1154,6 +1155,9 @@ static void lm90_report_alarms(struct work_struct *work) + + static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + { ++ if (data->shutdown) ++ return 0; ++ + if (force || !data->alarms_valid || + time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { + struct i2c_client *client = data->client; +@@ -2584,15 +2588,23 @@ static void lm90_restore_conf(void *_data) + struct lm90_data *data = _data; + struct i2c_client *client = data->client; + +- cancel_delayed_work_sync(&data->alert_work); +- cancel_work_sync(&data->report_work); +- + /* Restore initial configuration */ + if (data->flags & LM90_HAVE_CONVRATE) + lm90_write_convrate(data, data->convrate_orig); + lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); + } + ++static void lm90_stop_work(void *_data) ++{ ++ struct lm90_data *data = _data; ++ ++ hwmon_lock(data->hwmon_dev); ++ data->shutdown = true; ++ hwmon_unlock(data->hwmon_dev); ++ cancel_delayed_work_sync(&data->alert_work); ++ cancel_work_sync(&data->report_work); ++} ++ + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + { + struct device_node *np = client->dev.of_node; +@@ -2902,6 +2914,10 @@ static int lm90_probe(struct i2c_client *client) + + data->hwmon_dev = hwmon_dev; + ++ err = devm_add_action_or_reset(&client->dev, lm90_stop_work, data); ++ if (err) ++ return err; ++ + if (client->irq) { + dev_dbg(dev, "IRQ: %d\n", client->irq); + err = devm_request_threaded_irq(dev, client->irq, +@@ -2930,7 +2946,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, + */ + struct lm90_data *data = i2c_get_clientdata(client); + +- if ((data->flags & LM90_HAVE_BROKEN_ALERT) && ++ if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); +-- +2.53.0 + diff --git a/queue-7.0/ice-dpll-fix-misplaced-header-macros.patch b/queue-7.0/ice-dpll-fix-misplaced-header-macros.patch new file mode 100644 index 0000000000..a4111a6c6c --- /dev/null +++ b/queue-7.0/ice-dpll-fix-misplaced-header-macros.patch @@ -0,0 +1,74 @@ +From e4882fc1a390d834087a1dbbd62cb2999179975c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:17 -0700 +Subject: ice: dpll: fix misplaced header macros + +From: Ivan Vecera + +[ Upstream commit 30f1658fc5387384c7a60b9d15c79cb959512c1a ] + +The CGU register definitions (ICE_CGU_R10, ICE_CGU_R11 and related field +masks) were placed after the #endif of the _ICE_DPLL_H_ include guard, +leaving them unprotected. Move them inside the guard. + +Fixes: ad1df4f2d591 ("ice: dpll: Support E825-C SyncE and dynamic pin discovery") +Signed-off-by: Ivan Vecera +Reviewed-by: Aleksandr Loktionov +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-8-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dpll.h | 32 +++++++++++------------ + 1 file changed, 16 insertions(+), 16 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h +index ae42cdea0ee14..8678575359b92 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dpll.h ++++ b/drivers/net/ethernet/intel/ice/ice_dpll.h +@@ -8,6 +8,22 @@ + + #define ICE_DPLL_RCLK_NUM_MAX 4 + ++#define ICE_CGU_R10 0x28 ++#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) ++#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) ++#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) ++#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) ++#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) ++#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) ++#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) ++#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) ++#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) ++ ++#define ICE_CGU_R11 0x2C ++#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) ++ ++#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 ++ + /** + * enum ice_dpll_pin_sw - enumerate ice software pin indices: + * @ICE_DPLL_PIN_SW_1_IDX: index of first SW pin +@@ -157,19 +173,3 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { } + #endif + + #endif +- +-#define ICE_CGU_R10 0x28 +-#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) +-#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) +-#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) +-#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) +-#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) +-#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) +-#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) +-#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) +-#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) +- +-#define ICE_CGU_R11 0x2C +-#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) +- +-#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 +-- +2.53.0 + diff --git a/queue-7.0/ice-dpll-fix-rclk-pin-state-get-for-e810.patch b/queue-7.0/ice-dpll-fix-rclk-pin-state-get-for-e810.patch new file mode 100644 index 0000000000..6d8e5b2678 --- /dev/null +++ b/queue-7.0/ice-dpll-fix-rclk-pin-state-get-for-e810.patch @@ -0,0 +1,58 @@ +From abc89ac92a8d9f6f36bd2536fb6d36292d6c2ed9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:16 -0700 +Subject: ice: dpll: fix rclk pin state get for E810 + +From: Ivan Vecera + +[ Upstream commit cce709d8df6ba6d2a0a0dbf34acc2cdd9e23bd46 ] + +The refactoring of ice_dpll_rclk_state_on_pin_get() to use +ice_dpll_pin_get_parent_idx() omitted the base_rclk_idx adjustment that was +correctly added in the ice_dpll_rclk_state_on_pin_set() path. This breaks +E810 devices where base_rclk_idx is non-zero, causing the wrong hardware +index to be used for pin state lookup and incorrect recovered clock state +to be reported via the DPLL subsystem. E825C is unaffected as its +base_rclk_idx is 0. + +While at it, add bounds check against ICE_DPLL_RCLK_NUM_MAX on hw_idx after +the base_rclk_idx subtraction in both ice_dpll_rclk_state_on_pin_{get,set}() +to prevent out-of-bounds access on the pin state array. + +Fixes: ad1df4f2d591 ("ice: dpll: Support E825-C SyncE and dynamic pin discovery") +Signed-off-by: Ivan Vecera +Reviewed-by: Aleksandr Loktionov +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-7-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dpll.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c +index 27b460926bace..892bc7c2e28b4 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dpll.c ++++ b/drivers/net/ethernet/intel/ice/ice_dpll.c +@@ -2523,6 +2523,8 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv, + if (hw_idx < 0) + goto unlock; + hw_idx -= pf->dplls.base_rclk_idx; ++ if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) ++ goto unlock; + + if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) || + (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) { +@@ -2586,6 +2588,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv, + hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin); + if (hw_idx < 0) + goto unlock; ++ hw_idx -= pf->dplls.base_rclk_idx; ++ if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) ++ goto unlock; + + ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT, + extack); +-- +2.53.0 + diff --git a/queue-7.0/ice-fix-locking-in-ice_dcb_rebuild.patch b/queue-7.0/ice-fix-locking-in-ice_dcb_rebuild.patch new file mode 100644 index 0000000000..cee8a56c83 --- /dev/null +++ b/queue-7.0/ice-fix-locking-in-ice_dcb_rebuild.patch @@ -0,0 +1,57 @@ +From 4fd6c4183c2b9f2bb6f272b5102bdc74bdb13fc6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:15 -0700 +Subject: ice: fix locking in ice_dcb_rebuild() + +From: Bart Van Assche + +[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] + +Move the mutex_lock() call up to prevent that DCB settings change after +the first ice_query_port_ets() call. The second ice_query_port_ets() +call in ice_dcb_rebuild() is already protected by pf->tc_mutex. + +This also fixes a bug in an error path, as before taking the first +"goto dcb_error" in the function jumped over mutex_lock() to +mutex_unlock(). + +This bug has been detected by the clang thread-safety analyzer. + +Cc: intel-wired-lan@lists.osuosl.org +Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") +Signed-off-by: Bart Van Assche +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Tested-by: Arpana Arland +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +index 16aa255351523..0bc6dd3756879 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) + struct ice_dcbx_cfg *err_cfg; + int ret; + ++ mutex_lock(&pf->tc_mutex); ++ + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); + if (ret) { + dev_err(dev, "Query Port ETS failed\n"); + goto dcb_error; + } + +- mutex_lock(&pf->tc_mutex); +- + if (!pf->hw.port_info->qos_cfg.is_sw_lldp) + ice_cfg_etsrec_defaults(pf->hw.port_info); + +-- +2.53.0 + diff --git a/queue-7.0/ice-fix-setting-rss-vsi-hash-for-e830.patch b/queue-7.0/ice-fix-setting-rss-vsi-hash-for-e830.patch new file mode 100644 index 0000000000..423a064fb2 --- /dev/null +++ b/queue-7.0/ice-fix-setting-rss-vsi-hash-for-e830.patch @@ -0,0 +1,56 @@ +From a280c91b0be6dde3f9ab24a51467a75df62bfcbf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:14 -0700 +Subject: ice: fix setting RSS VSI hash for E830 + +From: Marcin Szycik + +[ Upstream commit b3cda96feb60d91fe88d52b974ff110dcfa91239 ] + +ice_set_rss_hfunc() performs a VSI update, in which it sets hashing +function, leaving other VSI options unchanged. However, ::q_opt_flags is +mistakenly set to the value of another field, instead of its original +value, probably due to a typo. What happens next is hardware-dependent: + +On E810, only the first bit is meaningful (see +ICE_AQ_VSI_Q_OPT_PE_FLTR_EN) and can potentially end up in a different +state than before VSI update. + +On E830, some of the remaining bits are not reserved. Setting them +to some unrelated values can cause the firmware to reject the update +because of invalid settings, or worse - succeed. + +Reproducer: + sudo ethtool -X $PF1 equal 8 + +Output in dmesg: + Failed to configure RSS hash for VSI 6, error -5 + +Fixes: 352e9bf23813 ("ice: enable symmetric-xor RSS for Toeplitz hash function") +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Przemek Kitszel +Signed-off-by: Marcin Szycik +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index 4718799263721..b5df8e052467a 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -8052,7 +8052,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) + ctx->info.q_opt_rss |= + FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); + ctx->info.q_opt_tc = vsi->info.q_opt_tc; +- ctx->info.q_opt_flags = vsi->info.q_opt_rss; ++ ctx->info.q_opt_flags = vsi->info.q_opt_flags; + + err = ice_update_vsi(hw, vsi->idx, ctx, NULL); + if (err) { +-- +2.53.0 + diff --git a/queue-7.0/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch b/queue-7.0/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch new file mode 100644 index 0000000000..5993e20a7d --- /dev/null +++ b/queue-7.0/ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch @@ -0,0 +1,79 @@ +From 30d8f0e267597f39f03676fd192054da54437209 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:11 -0700 +Subject: ice: ptp: serialize E825 PHY timer start with PTP lock + +From: Grzegorz Nitka + +[ Upstream commit 781ff8f2d575a794a2a4f11605288ae06757f5eb ] + +ice_start_phy_timer_eth56g() programs TIMETUS registers and issues +INIT_INCVAL without holding the global PTP semaphore. + +This allows concurrent PTP command paths to interleave with PHY timer +start, which can make the sequence fail and leave timer initialization +inconsistent. + +Take the PTP lock around TIMETUS registers programming and INIT_INCVAL +command execution, and make sure the lock is released on all error paths. + +Keep the subsequent sync step outside of this critical section, since +ice_sync_phy_timer_eth56g() takes the same semaphore internally. + +Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products") +Reviewed-by: Arkadiusz Kubalewski +Signed-off-by: Grzegorz Nitka +Reviewed-by: Aleksandr Loktionov +Tested-by: Alexander Nowlin +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +index 672218e5d1f94..8bb94e785f2a8 100644 +--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c ++++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +@@ -2141,16 +2141,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + } + incval = (u64)hi << 32 | lo; + ++ if (!ice_ptp_lock(hw)) { ++ dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n"); ++ return -EBUSY; ++ } ++ + err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval); + if (err) +- return err; ++ goto err_ptp_unlock; + + err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL); + if (err) +- return err; ++ goto err_ptp_unlock; + + ice_ptp_exec_tmr_cmd(hw); + ++ ice_ptp_unlock(hw); ++ + err = ice_sync_phy_timer_eth56g(hw, port); + if (err) + return err; +@@ -2166,6 +2173,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) + ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port); + + return 0; ++ ++err_ptp_unlock: ++ ice_ptp_unlock(hw); ++ return err; + } + + /** +-- +2.53.0 + diff --git a/queue-7.0/ice-ptp-use-primary-nac-semaphore-on-e825.patch b/queue-7.0/ice-ptp-use-primary-nac-semaphore-on-e825.patch new file mode 100644 index 0000000000..1c789de939 --- /dev/null +++ b/queue-7.0/ice-ptp-use-primary-nac-semaphore-on-e825.patch @@ -0,0 +1,69 @@ +From fdcad553fd204fd145862411d472d9fca434c517 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:12 -0700 +Subject: ice: ptp: use primary NAC semaphore on E825 + +From: Grzegorz Nitka + +[ Upstream commit 7b28523546c7e4adbb8436f2986efcfc8382985e ] + +For E825 2xNAC configurations, PTP semaphore operations must hit the +primary NAC register block so both sides coordinate on the same lock. + +Commit e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 +devices") updated other primary-only PTP register accesses to +use the primary NAC on non-primary functions, but left ice_ptp_lock() +and ice_ptp_unlock() operating on the local NAC. As a result, secondary +NAC PTP paths can take a different semaphore than the primary side. + +Select the primary hardware in ice_ptp_lock() and ice_ptp_unlock() when +the current function is not primary, keeping semaphore operations +symmetric and consistent with the rest of the 2xNAC PTP register access +path. + +Fixes: e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 devices") +Reviewed-by: Arkadiusz Kubalewski +Signed-off-by: Grzegorz Nitka +Reviewed-by: Aleksandr Loktionov +Tested-by: Alexander Nowlin +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-6-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +index 8bb94e785f2a8..2c18e16fe053e 100644 +--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c ++++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +@@ -5264,9 +5264,13 @@ static void ice_ptp_init_phy_e830(struct ice_ptp_hw *ptp) + */ + bool ice_ptp_lock(struct ice_hw *hw) + { ++ struct ice_pf *pf = container_of(hw, struct ice_pf, hw); + u32 hw_lock; + int i; + ++ if (!ice_is_primary(hw)) ++ hw = ice_get_primary_hw(pf); ++ + #define MAX_TRIES 15 + + for (i = 0; i < MAX_TRIES; i++) { +@@ -5293,6 +5297,11 @@ bool ice_ptp_lock(struct ice_hw *hw) + */ + void ice_ptp_unlock(struct ice_hw *hw) + { ++ struct ice_pf *pf = container_of(hw, struct ice_pf, hw); ++ ++ if (!ice_is_primary(hw)) ++ hw = ice_get_primary_hw(pf); ++ + wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); + } + +-- +2.53.0 + diff --git a/queue-7.0/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch b/queue-7.0/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch new file mode 100644 index 0000000000..23e80267de --- /dev/null +++ b/queue-7.0/idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch @@ -0,0 +1,91 @@ +From 8c0fd6f0567995751f4b01f4d6d7cd08c20c6443 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 14:48:12 -0700 +Subject: idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init() + +From: Emil Tantilov + +[ Upstream commit da4f76b6a84ede14a71282ef841768299ead0221 ] + +In idpf_ptp_init(), read_dev_clk_lock is initialized after +ptp_schedule_worker() had already been called (and after +idpf_ptp_settime64() could reach the lock). The PTP aux worker +fires immediately upon scheduling and can call into +idpf_ptp_read_src_clk_reg_direct(), which takes +spin_lock(&ptp->read_dev_clk_lock) on an uninitialized lock, triggering +the lockdep "non-static key" warning: + +[12973.796587] idpf 0000:83:00.0: Device HW Reset initiated +[12974.094507] INFO: trying to register non-static key. +... +[12974.097208] Call Trace: +[12974.097213] +[12974.097218] dump_stack_lvl+0x93/0xe0 +[12974.097234] register_lock_class+0x4c4/0x4e0 +[12974.097249] ? __lock_acquire+0x427/0x2290 +[12974.097259] __lock_acquire+0x98/0x2290 +[12974.097272] lock_acquire+0xc6/0x310 +[12974.097281] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097311] ? lockdep_hardirqs_on_prepare+0xde/0x190 +[12974.097318] ? finish_task_switch.isra.0+0xd2/0x350 +[12974.097330] ? __pfx_ptp_aux_kworker+0x10/0x10 [ptp] +[12974.097343] _raw_spin_lock+0x30/0x40 +[12974.097353] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097373] idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] +[12974.097391] ? kthread_worker_fn+0x88/0x3d0 +[12974.097404] ? kthread_worker_fn+0x4e/0x3d0 +[12974.097411] idpf_ptp_update_cached_phctime+0x26/0x120 [idpf] +[12974.097428] ? _raw_spin_unlock_irq+0x28/0x50 +[12974.097436] idpf_ptp_do_aux_work+0x15/0x20 [idpf] +[12974.097454] ptp_aux_kworker+0x20/0x40 [ptp] +[12974.097464] kthread_worker_fn+0xd5/0x3d0 +[12974.097474] ? __pfx_kthread_worker_fn+0x10/0x10 +[12974.097482] kthread+0xf4/0x130 +[12974.097489] ? __pfx_kthread+0x10/0x10 +[12974.097498] ret_from_fork+0x32c/0x410 +[12974.097512] ? __pfx_kthread+0x10/0x10 +[12974.097519] ret_from_fork_asm+0x1a/0x30 +[12974.097540] + +Move the call to spin_lock_init() up a bit to make sure read_dev_clk_lock +is not touched before it's been initialized. + +Fixes: 5cb8805d2366 ("idpf: negotiate PTP capabilities and get PTP clock") +Signed-off-by: Emil Tantilov +Reviewed-by: Madhu Chittim +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Simon Horman +Tested-by: Samuel Salin +Signed-off-by: Jacob Keller +Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-3-a5ea4dc837a9@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c +index eec91c4f0a75a..4a51d2727547d 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c +@@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) + goto free_ptp; + } + ++ spin_lock_init(&adapter->ptp->read_dev_clk_lock); ++ + err = idpf_ptp_create_clock(adapter); + if (err) + goto free_ptp; +@@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) + goto remove_clock; + } + +- spin_lock_init(&adapter->ptp->read_dev_clk_lock); +- + pci_dbg(adapter->pdev, "PTP init successful\n"); + + return 0; +-- +2.53.0 + diff --git a/queue-7.0/igc-set-tx-buffer-type-for-smd-frames.patch b/queue-7.0/igc-set-tx-buffer-type-for-smd-frames.patch new file mode 100644 index 0000000000..aefa3e2172 --- /dev/null +++ b/queue-7.0/igc-set-tx-buffer-type-for-smd-frames.patch @@ -0,0 +1,46 @@ +From 726e1d9f0b2d2070df716ca3391e3b0ef07af32d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 11:24:15 -0700 +Subject: igc: set tx buffer type for SMD frames + +From: Kohei Enju + +[ Upstream commit 5acc641e590e008caaed480ed9ffae47cf7ecbdf ] + +Sashiko pointed out that igc_fpe_init_smd_frame() initializes +igc_tx_buffer fields for an SMD skb, but does not set the buffer type: +https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp + +Since igc_tx_buffer entries are reused, a stale XDP or XSK type can +remain and make TX completion use the wrong cleanup path. + +Set the buffer type to IGC_TX_BUFFER_TYPE_SKB. + +Fixes: 5422570c0010 ("igc: add support for frame preemption verification") +Signed-off-by: Kohei Enju +Reviewed-by: Aleksandr Loktionov +Reviewed-by: Simon Horman +Tested-by: Avigail Dahan +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-9-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/igc/igc_tsn.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c +index 02dd9f0290a34..52de2bcbadbec 100644 +--- a/drivers/net/ethernet/intel/igc/igc_tsn.c ++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c +@@ -34,6 +34,7 @@ static int igc_fpe_init_smd_frame(struct igc_ring *ring, + return -ENOMEM; + } + ++ buffer->type = IGC_TX_BUFFER_TYPE_SKB; + buffer->skb = skb; + buffer->protocol = 0; + buffer->bytecount = skb->len; +-- +2.53.0 + diff --git a/queue-7.0/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch b/queue-7.0/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch new file mode 100644 index 0000000000..905f82ae33 --- /dev/null +++ b/queue-7.0/io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch @@ -0,0 +1,89 @@ +From 113abcffa9fa3d7efd2925eff93cdfda6da94bd6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 10:19:09 -0600 +Subject: io_uring/net: punt IORING_OP_BIND async if it needs file create + +From: Jens Axboe + +[ Upstream commit ccd25890f73c082fe2657ed227b497d6ac5fdc40 ] + +For two reasons: + +1) An opcode cannot block inside io_uring_enter() doing submissions, as + it'll stall the submission side pipeline. + +2) Ending up in sb_start_write() -> __sb_start_write() -> + percpu_down_read_freezable() introduces a new lockdep edge, which it + correctly complains about. + +Check if the socket type is AF_UNIX and has a non-empty pathname. If it +does, mark it REQ_F_FORCE_ASYNC to punt the submission to io-wq rather +than attempt to do it inline. + +Fixes: 7481fd93fa0a ("io_uring: Introduce IORING_OP_BIND") +Reviewed-by: Gabriel Krisman Bertazi +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/net.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/io_uring/net.c b/io_uring/net.c +index 8885d944130a1..5bd3fa5a2b6d3 100644 +--- a/io_uring/net.c ++++ b/io_uring/net.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1841,11 +1842,29 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) + return IOU_COMPLETE; + } + ++/* ++ * Check if bind request would potentially end up with filename_create(), ++ * which in turn end up in mnt_want_write() which will grab the fs ++ * percpu start write sem. This can trigger a lockdep warning. ++ */ ++static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len) ++{ ++ const struct sockaddr_un *sun; ++ ++ if (io->addr.ss_family != AF_UNIX) ++ return 0; ++ if (addr_len <= offsetof(struct sockaddr_un, sun_path)) ++ return 0; ++ sun = (const struct sockaddr_un *) &io->addr; ++ return sun->sun_path[0] != '\0'; ++} ++ + int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + { + struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); + struct sockaddr __user *uaddr; + struct io_async_msghdr *io; ++ int ret; + + if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) + return -EINVAL; +@@ -1856,7 +1875,12 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + io = io_msg_alloc_async(req); + if (unlikely(!io)) + return -ENOMEM; +- return move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); ++ if (unlikely(ret)) ++ return ret; ++ if (io_bind_file_create(io, bind->addr_len)) ++ req->flags |= REQ_F_FORCE_ASYNC; ++ return 0; + } + + int io_bind(struct io_kiocb *req, unsigned int issue_flags) +-- +2.53.0 + diff --git a/queue-7.0/io_uring-nop-pass-all-errors-to-userspace.patch b/queue-7.0/io_uring-nop-pass-all-errors-to-userspace.patch new file mode 100644 index 0000000000..a94d347dc5 --- /dev/null +++ b/queue-7.0/io_uring-nop-pass-all-errors-to-userspace.patch @@ -0,0 +1,42 @@ +From 7265490bd4fdbf2ad63e2fc2625e9f68f3da40ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:00:44 +0200 +Subject: io_uring/nop: pass all errors to userspace + +From: Alexander A. Klimov + +[ Upstream commit e97ff8b62d4690c69297f0f6de874f0564cc01a4 ] + +This fixes an inconsistency where io_nop() called req_set_fail() +based on ret, but passed just nop->result to userspace. +Originally, ret is a even copy of nop->result, but is set to an error +when such happens subsequently. Now that's also passed to userspace. + +Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") +Signed-off-by: Alexander A. Klimov +Link: https://patch.msgid.link/20260520180045.538533-1-grandmaster@al2klimov.de +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/nop.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/io_uring/nop.c b/io_uring/nop.c +index 3caf07878f8ac..f5c9969e7f64a 100644 +--- a/io_uring/nop.c ++++ b/io_uring/nop.c +@@ -79,9 +79,9 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) + if (ret < 0) + req_set_fail(req); + if (nop->flags & IORING_NOP_CQE32) +- io_req_set_res32(req, nop->result, 0, nop->extra1, nop->extra2); ++ io_req_set_res32(req, ret, 0, nop->extra1, nop->extra2); + else +- io_req_set_res(req, nop->result, 0); ++ io_req_set_res(req, ret, 0); + if (nop->flags & IORING_NOP_TW) { + req->io_task_work.func = io_req_task_complete; + io_req_task_work_add(req); +-- +2.53.0 + diff --git a/queue-7.0/io_uring-propagate-array_index_nospec-opcode-into-re.patch b/queue-7.0/io_uring-propagate-array_index_nospec-opcode-into-re.patch new file mode 100644 index 0000000000..b88f435e4e --- /dev/null +++ b/queue-7.0/io_uring-propagate-array_index_nospec-opcode-into-re.patch @@ -0,0 +1,70 @@ +From 518631ee8cd994ce89a2f0da5143f8dcbfc914c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 17:30:10 -0400 +Subject: io_uring: propagate array_index_nospec opcode into req->opcode + +From: Michael Bommarito + +[ Upstream commit cf18e36455603d65d4745de83e2d1743c54ada47 ] + +Commit 1e988c3fe126 ("io_uring: prevent opcode speculation") added +array_index_nospec() to io_init_req(), but applied it only to a local +opcode variable. req->opcode is initialized from sqe->opcode before the +bounds check and remains the raw value. + +Keep req->opcode as the canonical opcode in io_init_req(): reject +out-of-range values architecturally, then write the array_index_nospec() +result back to req->opcode before any table lookup. This keeps downstream +users of req->opcode from observing the raw user byte on a mispredicted +path. + +No functional change: array_index_nospec() is a no-op for opcodes in +[0, IORING_OP_LAST), and out-of-range opcodes are still rejected at the +bounds check above the assignment. + +Fixes: 1e988c3fe126 ("io_uring: prevent opcode speculation") +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Link: https://patch.msgid.link/20260517213010.696135-1-michael.bommarito@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/io_uring.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c +index 97260bca67e7b..cc4011d843377 100644 +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -1719,10 +1719,9 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req, + const struct io_issue_def *def; + unsigned int sqe_flags; + int personality; +- u8 opcode; + + req->ctx = ctx; +- req->opcode = opcode = READ_ONCE(sqe->opcode); ++ req->opcode = READ_ONCE(sqe->opcode); + /* same numerical values with corresponding REQ_F_*, safe to copy */ + sqe_flags = READ_ONCE(sqe->flags); + req->flags = (__force io_req_flags_t) sqe_flags; +@@ -1732,13 +1731,13 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req, + req->cancel_seq_set = false; + req->async_data = NULL; + +- if (unlikely(opcode >= IORING_OP_LAST)) { ++ if (unlikely(req->opcode >= IORING_OP_LAST)) { + req->opcode = 0; + return io_init_fail_req(req, -EINVAL); + } +- opcode = array_index_nospec(opcode, IORING_OP_LAST); ++ req->opcode = array_index_nospec(req->opcode, IORING_OP_LAST); + +- def = &io_issue_defs[opcode]; ++ def = &io_issue_defs[req->opcode]; + if (def->is_128 && !(ctx->flags & IORING_SETUP_SQE128)) { + /* + * A 128b op on a non-128b SQ requires mixed SQE support as +-- +2.53.0 + diff --git a/queue-7.0/iommu-fix-loss-of-errno-on-map-failure-for-classic-o.patch b/queue-7.0/iommu-fix-loss-of-errno-on-map-failure-for-classic-o.patch new file mode 100644 index 0000000000..2e690f48ff --- /dev/null +++ b/queue-7.0/iommu-fix-loss-of-errno-on-map-failure-for-classic-o.patch @@ -0,0 +1,52 @@ +From ed1b2addc9a89c67c6af1424b37ff2ff9c0225d0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:46:13 -0300 +Subject: iommu: Fix loss of errno on map failure for classic ops + +From: Jason Gunthorpe + +[ Upstream commit 6fc7e8a3b8115294f60f5c89de27330bf1b9c98e ] + +A typo, likely from a rebase, inverted the condition and caused +errors to be lost. Fix it to be "if (ret)". + +This was breaking iommu_create_device_direct_mappings() on drivers +that don't use iommupt and don't fully set up their domain in +alloc_pages() (i.e., SMMUv2). In this case the first call of +iommu_create_device_direct_mappings() should fail due to the +incompletely initialized domain. Since it wrongly returns success, +the second call to iommu_create_device_direct_mappings() doesn't +happen and IOMMU_RESV_DIRECT is never set up. + +Cc: stable@vger.kernel.org +Fixes: d6c65b0fd621 ("iommupt: Avoid rewalking during map") +Reported-by: Josua Mayer +Closes: https://lore.kernel.org/all/321c2e57-6a17-4aef-ba42-d2ebd577e472@solid-run.com/ +Signed-off-by: Jason Gunthorpe +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Samiullah Khawaja +Reviewed-by: Mostafa Saleh +Tested-by: Josua Mayer +Signed-off-by: Joerg Roedel +Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled") +Signed-off-by: Sasha Levin +--- + drivers/iommu/iommu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 973be8e2ab4c8..84ae594824a55 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -2709,7 +2709,7 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + return 0; + } + ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, gfp); +- if (!ret) ++ if (ret) + return ret; + + trace_map(iova, paddr, size); +-- +2.53.0 + diff --git a/queue-7.0/iommu-fix-up-map-unmap-debugging-for-iommupt-domains.patch b/queue-7.0/iommu-fix-up-map-unmap-debugging-for-iommupt-domains.patch new file mode 100644 index 0000000000..b6efe44a26 --- /dev/null +++ b/queue-7.0/iommu-fix-up-map-unmap-debugging-for-iommupt-domains.patch @@ -0,0 +1,141 @@ +From b1a36321d95b8014d62b6378faa9beb2beee77a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:46:14 -0300 +Subject: iommu: Fix up map/unmap debugging for iommupt domains + +From: Jason Gunthorpe + +[ Upstream commit b948a87228482235afbaf5f4d8037860b5c470fd ] + +Sashiko noticed a few issues in this path, and a few more were +found on review. Tidy them up further. These are intertwined +because the debug code depends on some of the WARN_ONs to function +right: + +Lift into iommu_map_nosync(): +- The might_sleep_if() +- 0 pgsize_bitmap WARN_ON +- Promote the illegal domain->type to a WARN_ON +- WARN_ON for illegal gfp flags + +Then remove the return 0 since it is now safe to call +iommu_debug_map(). + +Lift into __iommu_unmap(): +- 0 pgsize_bitmap WARN_ON +- Promote the illegal domain->type to a WARN_ON +- iommu_debug_unmap_begin() + +This now pairs with the unconditional iommu_debug_map() on the +mapping side. Thus iommu debugging now works for iommupt along +with some of the other debugging features. + +Fixes: 99fb8afa16ad ("iommupt: Directly call iommupt's unmap_range()") +Fixes: d6c65b0fd621 ("iommupt: Avoid rewalking during map") +Signed-off-by: Jason Gunthorpe +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Samiullah Khawaja +Reviewed-by: Mostafa Saleh +Tested-by: Josua Mayer +Signed-off-by: Joerg Roedel +Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled") +Signed-off-by: Sasha Levin +--- + drivers/iommu/iommu.c | 43 ++++++++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 21 deletions(-) + +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 84ae594824a55..0c2a4beb6ac32 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -2623,19 +2623,9 @@ static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, + size_t orig_size = size; + int ret = 0; + +- might_sleep_if(gfpflags_allow_blocking(gfp)); +- +- if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) +- return -EINVAL; +- +- if (WARN_ON(!ops->map_pages || domain->pgsize_bitmap == 0UL)) ++ if (WARN_ON(!ops->map_pages)) + return -ENODEV; + +- /* Discourage passing strange GFP flags */ +- if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 | +- __GFP_HIGHMEM))) +- return -EINVAL; +- + /* find out the minimum page size supported */ + min_pagesz = 1 << __ffs(domain->pgsize_bitmap); + +@@ -2697,6 +2687,15 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + struct pt_iommu *pt = iommupt_from_domain(domain); + int ret; + ++ might_sleep_if(gfpflags_allow_blocking(gfp)); ++ ++ /* Discourage passing strange GFP flags or illegal domains */ ++ if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) || ++ !domain->pgsize_bitmap || ++ (gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 | ++ __GFP_HIGHMEM)))) ++ return -EINVAL; ++ + if (pt) { + size_t mapped = 0; + +@@ -2706,11 +2705,12 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + iommu_unmap(domain, iova, mapped); + return ret; + } +- return 0; ++ } else { ++ ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, ++ gfp); ++ if (ret) ++ return ret; + } +- ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, gfp); +- if (ret) +- return ret; + + trace_map(iova, paddr, size); + iommu_debug_map(domain, paddr, size); +@@ -2742,10 +2742,7 @@ __iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, + size_t unmapped_page, unmapped = 0; + unsigned int min_pagesz; + +- if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) +- return 0; +- +- if (WARN_ON(!ops->unmap_pages || domain->pgsize_bitmap == 0UL)) ++ if (WARN_ON(!ops->unmap_pages)) + return 0; + + /* find out the minimum page size supported */ +@@ -2764,8 +2761,6 @@ __iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, + + pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size); + +- iommu_debug_unmap_begin(domain, iova, size); +- + /* + * Keep iterating until we either unmap 'size' bytes (or more) + * or we hit an area that isn't mapped. +@@ -2801,6 +2796,12 @@ static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova, + struct pt_iommu *pt = iommupt_from_domain(domain); + size_t unmapped; + ++ if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) || ++ !domain->pgsize_bitmap)) ++ return 0; ++ ++ iommu_debug_unmap_begin(domain, iova, size); ++ + if (pt) + unmapped = pt->ops->unmap_range(pt, iova, size, iotlb_gather); + else +-- +2.53.0 + diff --git a/queue-7.0/iommu-handle-unmap-error-when-iommu_debug-is-enabled.patch b/queue-7.0/iommu-handle-unmap-error-when-iommu_debug-is-enabled.patch new file mode 100644 index 0000000000..22b5e95094 --- /dev/null +++ b/queue-7.0/iommu-handle-unmap-error-when-iommu_debug-is-enabled.patch @@ -0,0 +1,134 @@ +From fb3146fa7943f7527d010f8fb4059d75ee8425ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:46:15 -0300 +Subject: iommu: Handle unmap error when iommu_debug is enabled + +From: Jason Gunthorpe + +[ Upstream commit 0735c54804c709d1b292f3b6947cfb560b2ce552 ] + +Sashiko noticed a latent bug where the map error flow called iommu_unmap() +which calls iommu_debug_unmap_begin()/iommu_debug_unmap_end() however +since this is an error path the map flow never actually established the +original iommu_debug_map() it will malfunction. + +Lift the unmap error handling into iommu_map_nosync() and reorder it so +the trace_map()/iommu_debug_map() records the partial mapping and then +immediately unmaps it. This avoid creating the unbalanced tracking and +provides saner tracing instead of a unmap unmatched to any map. + +Fixes: ccc21213f013 ("iommu: Add calls for IOMMU_DEBUG_PAGEALLOC") +Signed-off-by: Jason Gunthorpe +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Samiullah Khawaja +Reviewed-by: Mostafa Saleh +Tested-by: Josua Mayer +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + drivers/iommu/iommu.c | 49 +++++++++++++++++-------------------------- + 1 file changed, 19 insertions(+), 30 deletions(-) + +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 0c2a4beb6ac32..93c9081707401 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -2615,12 +2615,11 @@ static size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, + + static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, + unsigned long iova, phys_addr_t paddr, +- size_t size, int prot, gfp_t gfp) ++ size_t size, int prot, gfp_t gfp, ++ size_t *mapped) + { + const struct iommu_domain_ops *ops = domain->ops; +- unsigned long orig_iova = iova; + unsigned int min_pagesz; +- size_t orig_size = size; + int ret = 0; + + if (WARN_ON(!ops->map_pages)) +@@ -2643,31 +2642,25 @@ static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, + pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size); + + while (size) { +- size_t pgsize, count, mapped = 0; ++ size_t pgsize, count, op_mapped = 0; + + pgsize = iommu_pgsize(domain, iova, paddr, size, &count); + + pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx count %zu\n", + iova, &paddr, pgsize, count); + ret = ops->map_pages(domain, iova, paddr, pgsize, count, prot, +- gfp, &mapped); ++ gfp, &op_mapped); + /* + * Some pages may have been mapped, even if an error occurred, + * so we should account for those so they can be unmapped. + */ +- size -= mapped; +- ++ *mapped += op_mapped; + if (ret) +- break; +- +- iova += mapped; +- paddr += mapped; +- } ++ return ret; + +- /* unroll mapping in case something went wrong */ +- if (ret) { +- iommu_unmap(domain, orig_iova, orig_size - size); +- return ret; ++ size -= op_mapped; ++ iova += op_mapped; ++ paddr += op_mapped; + } + return 0; + } +@@ -2685,6 +2678,7 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot, gfp_t gfp) + { + struct pt_iommu *pt = iommupt_from_domain(domain); ++ size_t mapped = 0; + int ret; + + might_sleep_if(gfpflags_allow_blocking(gfp)); +@@ -2696,24 +2690,19 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + __GFP_HIGHMEM)))) + return -EINVAL; + +- if (pt) { +- size_t mapped = 0; +- ++ if (pt) + ret = pt->ops->map_range(pt, iova, paddr, size, prot, gfp, + &mapped); +- if (ret) { +- iommu_unmap(domain, iova, mapped); +- return ret; +- } +- } else { ++ else + ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, +- gfp); +- if (ret) +- return ret; +- } ++ gfp, &mapped); + +- trace_map(iova, paddr, size); +- iommu_debug_map(domain, paddr, size); ++ trace_map(iova, paddr, mapped); ++ iommu_debug_map(domain, paddr, mapped); ++ if (ret) { ++ iommu_unmap(domain, iova, mapped); ++ return ret; ++ } + return 0; + } + +-- +2.53.0 + diff --git a/queue-7.0/iommupt-avoid-rewalking-during-map.patch b/queue-7.0/iommupt-avoid-rewalking-during-map.patch new file mode 100644 index 0000000000..22b79188e4 --- /dev/null +++ b/queue-7.0/iommupt-avoid-rewalking-during-map.patch @@ -0,0 +1,493 @@ +From 99589b27fb3fe1bf0f9c0b255eec92f9ca23c50f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Feb 2026 15:30:11 -0400 +Subject: iommupt: Avoid rewalking during map + +From: Jason Gunthorpe + +[ Upstream commit d6c65b0fd6218bd21ed0be7a8d3218e8f6dc91de ] + +Currently the core code provides a simplified interface to drivers where +it fragments a requested multi-page map into single page size steps after +doing all the calculations to figure out what page size is +appropriate. Each step rewalks the page tables from the start. + +Since iommupt has a single implementation of the mapping algorithm it can +internally compute each step as it goes while retaining its current +position in the walk. + +Add a new function pt_pgsz_count() which computes the same page size +fragement of a large mapping operations. + +Compute the next fragment when all the leaf entries of the current +fragement have been written, then continue walking from the current +point. + +The function pointer is run through pt_iommu_ops instead of +iommu_domain_ops to discourage using it outside iommupt. All drivers with +their own page tables should continue to use the simplified map_pages() +style interfaces. + +Reviewed-by: Samiullah Khawaja +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Reviewed-by: Lu Baolu +Signed-off-by: Joerg Roedel +Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled") +Signed-off-by: Sasha Levin +--- + drivers/iommu/generic_pt/iommu_pt.h | 133 ++++++++++++-------- + drivers/iommu/generic_pt/kunit_generic_pt.h | 12 ++ + drivers/iommu/generic_pt/pt_iter.h | 22 ++++ + drivers/iommu/iommu.c | 39 ++++-- + include/linux/generic_pt/iommu.h | 34 ++++- + 5 files changed, 175 insertions(+), 65 deletions(-) + +diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h +index 8bc4683a64dc1..c0241b24a6098 100644 +--- a/drivers/iommu/generic_pt/iommu_pt.h ++++ b/drivers/iommu/generic_pt/iommu_pt.h +@@ -466,6 +466,7 @@ struct pt_iommu_map_args { + pt_oaddr_t oa; + unsigned int leaf_pgsize_lg2; + unsigned int leaf_level; ++ pt_vaddr_t num_leaves; + }; + + /* +@@ -518,11 +519,15 @@ static int clear_contig(const struct pt_state *start_pts, + static int __map_range_leaf(struct pt_range *range, void *arg, + unsigned int level, struct pt_table_p *table) + { ++ struct pt_iommu *iommu_table = iommu_from_common(range->common); + struct pt_state pts = pt_init(range, level, table); + struct pt_iommu_map_args *map = arg; + unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2; + unsigned int start_index; + pt_oaddr_t oa = map->oa; ++ unsigned int num_leaves; ++ unsigned int orig_end; ++ pt_vaddr_t last_va; + unsigned int step; + bool need_contig; + int ret = 0; +@@ -536,6 +541,15 @@ static int __map_range_leaf(struct pt_range *range, void *arg, + + _pt_iter_first(&pts); + start_index = pts.index; ++ orig_end = pts.end_index; ++ if (pts.index + map->num_leaves < pts.end_index) { ++ /* Need to stop in the middle of the table to change sizes */ ++ pts.end_index = pts.index + map->num_leaves; ++ num_leaves = 0; ++ } else { ++ num_leaves = map->num_leaves - (pts.end_index - pts.index); ++ } ++ + do { + pts.type = pt_load_entry_raw(&pts); + if (pts.type != PT_ENTRY_EMPTY || need_contig) { +@@ -561,7 +575,40 @@ static int __map_range_leaf(struct pt_range *range, void *arg, + flush_writes_range(&pts, start_index, pts.index); + + map->oa = oa; +- return ret; ++ map->num_leaves = num_leaves; ++ if (ret || num_leaves) ++ return ret; ++ ++ /* range->va is not valid if we reached the end of the table */ ++ pts.index -= step; ++ pt_index_to_va(&pts); ++ pts.index += step; ++ last_va = range->va + log2_to_int(leaf_pgsize_lg2); ++ ++ if (last_va - 1 == range->last_va) { ++ PT_WARN_ON(pts.index != orig_end); ++ return 0; ++ } ++ ++ /* ++ * Reached a point where the page size changed, compute the new ++ * parameters. ++ */ ++ map->leaf_pgsize_lg2 = pt_compute_best_pgsize( ++ iommu_table->domain.pgsize_bitmap, last_va, range->last_va, oa); ++ map->leaf_level = ++ pt_pgsz_lg2_to_level(range->common, map->leaf_pgsize_lg2); ++ map->num_leaves = pt_pgsz_count(iommu_table->domain.pgsize_bitmap, ++ last_va, range->last_va, oa, ++ map->leaf_pgsize_lg2); ++ ++ /* Didn't finish this table level, caller will repeat it */ ++ if (pts.index != orig_end) { ++ if (pts.index != start_index) ++ pt_index_to_va(&pts); ++ return -EAGAIN; ++ } ++ return 0; + } + + static int __map_range(struct pt_range *range, void *arg, unsigned int level, +@@ -584,14 +631,9 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level, + if (pts.type != PT_ENTRY_EMPTY) + return -EADDRINUSE; + ret = pt_iommu_new_table(&pts, &map->attrs); +- if (ret) { +- /* +- * Racing with another thread installing a table +- */ +- if (ret == -EAGAIN) +- continue; ++ /* EAGAIN on a race will loop again */ ++ if (ret) + return ret; +- } + } else { + pts.table_lower = pt_table_ptr(&pts); + /* +@@ -615,10 +657,12 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level, + * The already present table can possibly be shared with another + * concurrent map. + */ +- if (map->leaf_level == level - 1) +- ret = pt_descend(&pts, arg, __map_range_leaf); +- else +- ret = pt_descend(&pts, arg, __map_range); ++ do { ++ if (map->leaf_level == level - 1) ++ ret = pt_descend(&pts, arg, __map_range_leaf); ++ else ++ ret = pt_descend(&pts, arg, __map_range); ++ } while (ret == -EAGAIN); + if (ret) + return ret; + +@@ -626,6 +670,14 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level, + pt_index_to_va(&pts); + if (pts.index >= pts.end_index) + break; ++ ++ /* ++ * This level is currently running __map_range_leaf() which is ++ * not correct if the target level has been updated to this ++ * level. Have the caller invoke __map_range_leaf. ++ */ ++ if (map->leaf_level == level) ++ return -EAGAIN; + } while (true); + return 0; + } +@@ -797,12 +849,13 @@ static int check_map_range(struct pt_iommu *iommu_table, struct pt_range *range, + static int do_map(struct pt_range *range, struct pt_common *common, + bool single_page, struct pt_iommu_map_args *map) + { ++ int ret; ++ + /* + * The __map_single_page() fast path does not support DMA_INCOHERENT + * flushing to keep its .text small. + */ + if (single_page && !pt_feature(common, PT_FEAT_DMA_INCOHERENT)) { +- int ret; + + ret = pt_walk_range(range, __map_single_page, map); + if (ret != -EAGAIN) +@@ -810,50 +863,25 @@ static int do_map(struct pt_range *range, struct pt_common *common, + /* EAGAIN falls through to the full path */ + } + +- if (map->leaf_level == range->top_level) +- return pt_walk_range(range, __map_range_leaf, map); +- return pt_walk_range(range, __map_range, map); ++ do { ++ if (map->leaf_level == range->top_level) ++ ret = pt_walk_range(range, __map_range_leaf, map); ++ else ++ ret = pt_walk_range(range, __map_range, map); ++ } while (ret == -EAGAIN); ++ return ret; + } + +-/** +- * map_pages() - Install translation for an IOVA range +- * @domain: Domain to manipulate +- * @iova: IO virtual address to start +- * @paddr: Physical/Output address to start +- * @pgsize: Length of each page +- * @pgcount: Length of the range in pgsize units starting from @iova +- * @prot: A bitmap of IOMMU_READ/WRITE/CACHE/NOEXEC/MMIO +- * @gfp: GFP flags for any memory allocations +- * @mapped: Total bytes successfully mapped +- * +- * The range starting at IOVA will have paddr installed into it. The caller +- * must specify a valid pgsize and pgcount to segment the range into compatible +- * blocks. +- * +- * On error the caller will probably want to invoke unmap on the range from iova +- * up to the amount indicated by @mapped to return the table back to an +- * unchanged state. +- * +- * Context: The caller must hold a write range lock that includes the whole +- * range. +- * +- * Returns: -ERRNO on failure, 0 on success. The number of bytes of VA that were +- * mapped are added to @mapped, @mapped is not zerod first. +- */ +-int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova, +- phys_addr_t paddr, size_t pgsize, size_t pgcount, +- int prot, gfp_t gfp, size_t *mapped) ++static int NS(map_range)(struct pt_iommu *iommu_table, dma_addr_t iova, ++ phys_addr_t paddr, dma_addr_t len, unsigned int prot, ++ gfp_t gfp, size_t *mapped) + { +- struct pt_iommu *iommu_table = +- container_of(domain, struct pt_iommu, domain); + pt_vaddr_t pgsize_bitmap = iommu_table->domain.pgsize_bitmap; + struct pt_common *common = common_from_iommu(iommu_table); + struct iommu_iotlb_gather iotlb_gather; +- pt_vaddr_t len = pgsize * pgcount; + struct pt_iommu_map_args map = { + .iotlb_gather = &iotlb_gather, + .oa = paddr, +- .leaf_pgsize_lg2 = vaffs(pgsize), + }; + bool single_page = false; + struct pt_range range; +@@ -881,13 +909,13 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova, + return ret; + + /* Calculate target page size and level for the leaves */ +- if (pt_has_system_page_size(common) && pgsize == PAGE_SIZE && +- pgcount == 1) { ++ if (pt_has_system_page_size(common) && len == PAGE_SIZE) { + PT_WARN_ON(!(pgsize_bitmap & PAGE_SIZE)); + if (log2_mod(iova | paddr, PAGE_SHIFT)) + return -ENXIO; + map.leaf_pgsize_lg2 = PAGE_SHIFT; + map.leaf_level = 0; ++ map.num_leaves = 1; + single_page = true; + } else { + map.leaf_pgsize_lg2 = pt_compute_best_pgsize( +@@ -896,6 +924,9 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova, + return -ENXIO; + map.leaf_level = + pt_pgsz_lg2_to_level(common, map.leaf_pgsize_lg2); ++ map.num_leaves = pt_pgsz_count(pgsize_bitmap, range.va, ++ range.last_va, paddr, ++ map.leaf_pgsize_lg2); + } + + ret = check_map_range(iommu_table, &range, &map); +@@ -918,7 +949,6 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova, + *mapped += map.oa - paddr; + return ret; + } +-EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(map_pages), "GENERIC_PT_IOMMU"); + + struct pt_unmap_args { + struct iommu_pages_list free_list; +@@ -1087,6 +1117,7 @@ static void NS(deinit)(struct pt_iommu *iommu_table) + } + + static const struct pt_iommu_ops NS(ops) = { ++ .map_range = NS(map_range), + .unmap_range = NS(unmap_range), + #if IS_ENABLED(CONFIG_IOMMUFD_DRIVER) && defined(pt_entry_is_write_dirty) && \ + IS_ENABLED(CONFIG_IOMMUFD_TEST) && defined(pt_entry_make_write_dirty) +diff --git a/drivers/iommu/generic_pt/kunit_generic_pt.h b/drivers/iommu/generic_pt/kunit_generic_pt.h +index 68278bf15cfe0..374e475f591e1 100644 +--- a/drivers/iommu/generic_pt/kunit_generic_pt.h ++++ b/drivers/iommu/generic_pt/kunit_generic_pt.h +@@ -312,6 +312,17 @@ static void test_best_pgsize(struct kunit *test) + } + } + ++static void test_pgsz_count(struct kunit *test) ++{ ++ KUNIT_EXPECT_EQ(test, ++ pt_pgsz_count(SZ_4K, 0, SZ_1G - 1, 0, ilog2(SZ_4K)), ++ SZ_1G / SZ_4K); ++ KUNIT_EXPECT_EQ(test, ++ pt_pgsz_count(SZ_2M | SZ_4K, SZ_4K, SZ_1G - 1, SZ_4K, ++ ilog2(SZ_4K)), ++ (SZ_2M - SZ_4K) / SZ_4K); ++} ++ + /* + * Check that pt_install_table() and pt_table_pa() match + */ +@@ -770,6 +781,7 @@ static struct kunit_case generic_pt_test_cases[] = { + KUNIT_CASE_FMT(test_init), + KUNIT_CASE_FMT(test_bitops), + KUNIT_CASE_FMT(test_best_pgsize), ++ KUNIT_CASE_FMT(test_pgsz_count), + KUNIT_CASE_FMT(test_table_ptr), + KUNIT_CASE_FMT(test_max_va), + KUNIT_CASE_FMT(test_table_radix), +diff --git a/drivers/iommu/generic_pt/pt_iter.h b/drivers/iommu/generic_pt/pt_iter.h +index c0d8617cce292..3e45dbde6b832 100644 +--- a/drivers/iommu/generic_pt/pt_iter.h ++++ b/drivers/iommu/generic_pt/pt_iter.h +@@ -569,6 +569,28 @@ static inline unsigned int pt_compute_best_pgsize(pt_vaddr_t pgsz_bitmap, + return pgsz_lg2; + } + ++/* ++ * Return the number of pgsize_lg2 leaf entries that can be mapped for ++ * va to oa. This accounts for any requirement to reduce or increase the page ++ * size across the VA range. ++ */ ++static inline pt_vaddr_t pt_pgsz_count(pt_vaddr_t pgsz_bitmap, pt_vaddr_t va, ++ pt_vaddr_t last_va, pt_oaddr_t oa, ++ unsigned int pgsize_lg2) ++{ ++ pt_vaddr_t len = last_va - va + 1; ++ pt_vaddr_t next_pgsizes = log2_set_mod(pgsz_bitmap, 0, pgsize_lg2 + 1); ++ ++ if (next_pgsizes) { ++ unsigned int next_pgsize_lg2 = vaffs(next_pgsizes); ++ ++ if (log2_mod(va ^ oa, next_pgsize_lg2) == 0) ++ len = min(len, log2_set_mod_max(va, next_pgsize_lg2) - ++ va + 1); ++ } ++ return log2_div(len, pgsize_lg2); ++} ++ + #define _PT_MAKE_CALL_LEVEL(fn) \ + static __always_inline int fn(struct pt_range *range, void *arg, \ + unsigned int level, \ +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 04b1c0f358b05..973be8e2ab4c8 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -2613,14 +2613,14 @@ static size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, + return pgsize; + } + +-int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, +- phys_addr_t paddr, size_t size, int prot, gfp_t gfp) ++static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, ++ unsigned long iova, phys_addr_t paddr, ++ size_t size, int prot, gfp_t gfp) + { + const struct iommu_domain_ops *ops = domain->ops; + unsigned long orig_iova = iova; + unsigned int min_pagesz; + size_t orig_size = size; +- phys_addr_t orig_paddr = paddr; + int ret = 0; + + might_sleep_if(gfpflags_allow_blocking(gfp)); +@@ -2677,12 +2677,9 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, + /* unroll mapping in case something went wrong */ + if (ret) { + iommu_unmap(domain, orig_iova, orig_size - size); +- } else { +- trace_map(orig_iova, orig_paddr, orig_size); +- iommu_debug_map(domain, orig_paddr, orig_size); ++ return ret; + } +- +- return ret; ++ return 0; + } + + int iommu_sync_map(struct iommu_domain *domain, unsigned long iova, size_t size) +@@ -2694,6 +2691,32 @@ int iommu_sync_map(struct iommu_domain *domain, unsigned long iova, size_t size) + return ops->iotlb_sync_map(domain, iova, size); + } + ++int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, ++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp) ++{ ++ struct pt_iommu *pt = iommupt_from_domain(domain); ++ int ret; ++ ++ if (pt) { ++ size_t mapped = 0; ++ ++ ret = pt->ops->map_range(pt, iova, paddr, size, prot, gfp, ++ &mapped); ++ if (ret) { ++ iommu_unmap(domain, iova, mapped); ++ return ret; ++ } ++ return 0; ++ } ++ ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, gfp); ++ if (!ret) ++ return ret; ++ ++ trace_map(iova, paddr, size); ++ iommu_debug_map(domain, paddr, size); ++ return 0; ++} ++ + int iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot, gfp_t gfp) + { +diff --git a/include/linux/generic_pt/iommu.h b/include/linux/generic_pt/iommu.h +index f094f8f44e4e8..43cc98c9c55f7 100644 +--- a/include/linux/generic_pt/iommu.h ++++ b/include/linux/generic_pt/iommu.h +@@ -87,6 +87,33 @@ struct pt_iommu_info { + }; + + struct pt_iommu_ops { ++ /** ++ * @map_range: Install translation for an IOVA range ++ * @iommu_table: Table to manipulate ++ * @iova: IO virtual address to start ++ * @paddr: Physical/Output address to start ++ * @len: Length of the range starting from @iova ++ * @prot: A bitmap of IOMMU_READ/WRITE/CACHE/NOEXEC/MMIO ++ * @gfp: GFP flags for any memory allocations ++ * ++ * The range starting at IOVA will have paddr installed into it. The ++ * rage is automatically segmented into optimally sized table entries, ++ * and can have any valid alignment. ++ * ++ * On error the caller will probably want to invoke unmap on the range ++ * from iova up to the amount indicated by @mapped to return the table ++ * back to an unchanged state. ++ * ++ * Context: The caller must hold a write range lock that includes ++ * the whole range. ++ * ++ * Returns: -ERRNO on failure, 0 on success. The number of bytes of VA ++ * that were mapped are added to @mapped, @mapped is not zerod first. ++ */ ++ int (*map_range)(struct pt_iommu *iommu_table, dma_addr_t iova, ++ phys_addr_t paddr, dma_addr_t len, unsigned int prot, ++ gfp_t gfp, size_t *mapped); ++ + /** + * @unmap_range: Make a range of IOVA empty/not present + * @iommu_table: Table to manipulate +@@ -224,10 +251,6 @@ struct pt_iommu_cfg { + #define IOMMU_PROTOTYPES(fmt) \ + phys_addr_t pt_iommu_##fmt##_iova_to_phys(struct iommu_domain *domain, \ + dma_addr_t iova); \ +- int pt_iommu_##fmt##_map_pages(struct iommu_domain *domain, \ +- unsigned long iova, phys_addr_t paddr, \ +- size_t pgsize, size_t pgcount, \ +- int prot, gfp_t gfp, size_t *mapped); \ + int pt_iommu_##fmt##_read_and_clear_dirty( \ + struct iommu_domain *domain, unsigned long iova, size_t size, \ + unsigned long flags, struct iommu_dirty_bitmap *dirty); \ +@@ -248,8 +271,7 @@ struct pt_iommu_cfg { + * iommu_pt + */ + #define IOMMU_PT_DOMAIN_OPS(fmt) \ +- .iova_to_phys = &pt_iommu_##fmt##_iova_to_phys, \ +- .map_pages = &pt_iommu_##fmt##_map_pages ++ .iova_to_phys = &pt_iommu_##fmt##_iova_to_phys + #define IOMMU_PT_DIRTY_OPS(fmt) \ + .read_and_clear_dirty = &pt_iommu_##fmt##_read_and_clear_dirty + +-- +2.53.0 + diff --git a/queue-7.0/iommupt-check-for-missing-page_size-in-the-pgsize_bi.patch b/queue-7.0/iommupt-check-for-missing-page_size-in-the-pgsize_bi.patch new file mode 100644 index 0000000000..d7e7ddd84b --- /dev/null +++ b/queue-7.0/iommupt-check-for-missing-page_size-in-the-pgsize_bi.patch @@ -0,0 +1,45 @@ +From 8370e4a8ae7bcf0aaed54406874edc792cd20b5a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:46:16 -0300 +Subject: iommupt: Check for missing PAGE_SIZE in the pgsize_bitmap + +From: Jason Gunthorpe + +[ Upstream commit 8ef3f77c440005c7f04229a75976bfc078364247 ] + +Sashiko pointed out that the driver could drop PAGE_SIZE from the +pgsize_bitmap. That is technically allowed but nothing does it, and +such an iommu_domain would not be used with the DMA API today. + +Still, it is against the design and it is trivial to fix up. Lift +the PT_WARN_ON to the if branch and just skip the fast path. + +Fixes: dcd6a011a8d5 ("iommupt: Add map_pages op") +Signed-off-by: Jason Gunthorpe +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Samiullah Khawaja +Tested-by: Josua Mayer +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + drivers/iommu/generic_pt/iommu_pt.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h +index c0241b24a6098..4be33c45bedc9 100644 +--- a/drivers/iommu/generic_pt/iommu_pt.h ++++ b/drivers/iommu/generic_pt/iommu_pt.h +@@ -909,8 +909,8 @@ static int NS(map_range)(struct pt_iommu *iommu_table, dma_addr_t iova, + return ret; + + /* Calculate target page size and level for the leaves */ +- if (pt_has_system_page_size(common) && len == PAGE_SIZE) { +- PT_WARN_ON(!(pgsize_bitmap & PAGE_SIZE)); ++ if (pt_has_system_page_size(common) && len == PAGE_SIZE && ++ likely(pgsize_bitmap & PAGE_SIZE)) { + if (log2_mod(iova | paddr, PAGE_SHIFT)) + return -ENXIO; + map.leaf_pgsize_lg2 = PAGE_SHIFT; +-- +2.53.0 + diff --git a/queue-7.0/iommupt-directly-call-iommupt-s-unmap_range.patch b/queue-7.0/iommupt-directly-call-iommupt-s-unmap_range.patch new file mode 100644 index 0000000000..5156c84841 --- /dev/null +++ b/queue-7.0/iommupt-directly-call-iommupt-s-unmap_range.patch @@ -0,0 +1,239 @@ +From d23de98738ab4db31f9406e6b8dfc39c429c4663 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Feb 2026 15:30:10 -0400 +Subject: iommupt: Directly call iommupt's unmap_range() + +From: Jason Gunthorpe + +[ Upstream commit 99fb8afa16add85ed016baee9735231bca0c32b4 ] + +The common algorithm in iommupt does not require the iommu_pgsize() +calculations, it can directly unmap any arbitrary range. Add a new function +pointer to directly call an iommupt unmap_range op and make +__iommu_unmap() call it directly. + +Gives about a 5% gain on single page unmappings. + +The function pointer is run through pt_iommu_ops instead of +iommu_domain_ops to discourage using it outside iommupt. All drivers with +their own page tables should continue to use the simplified +map/unmap_pages() style interfaces. + +Reviewed-by: Samiullah Khawaja +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Reviewed-by: Lu Baolu +Signed-off-by: Joerg Roedel +Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled") +Signed-off-by: Sasha Levin +--- + drivers/iommu/generic_pt/iommu_pt.h | 29 ++++------------------ + drivers/iommu/iommu.c | 27 ++++++++++++++++----- + include/linux/generic_pt/iommu.h | 37 ++++++++++++++++++++++++----- + include/linux/iommu.h | 1 + + 4 files changed, 57 insertions(+), 37 deletions(-) + +diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h +index 7e7a6e7abdeed..8bc4683a64dc1 100644 +--- a/drivers/iommu/generic_pt/iommu_pt.h ++++ b/drivers/iommu/generic_pt/iommu_pt.h +@@ -1020,34 +1020,12 @@ static __maybe_unused int __unmap_range(struct pt_range *range, void *arg, + return ret; + } + +-/** +- * unmap_pages() - Make a range of IOVA empty/not present +- * @domain: Domain to manipulate +- * @iova: IO virtual address to start +- * @pgsize: Length of each page +- * @pgcount: Length of the range in pgsize units starting from @iova +- * @iotlb_gather: Gather struct that must be flushed on return +- * +- * unmap_pages() will remove a translation created by map_pages(). It cannot +- * subdivide a mapping created by map_pages(), so it should be called with IOVA +- * ranges that match those passed to map_pages(). The IOVA range can aggregate +- * contiguous map_pages() calls so long as no individual range is split. +- * +- * Context: The caller must hold a write range lock that includes +- * the whole range. +- * +- * Returns: Number of bytes of VA unmapped. iova + res will be the point +- * unmapping stopped. +- */ +-size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova, +- size_t pgsize, size_t pgcount, ++static size_t NS(unmap_range)(struct pt_iommu *iommu_table, dma_addr_t iova, ++ dma_addr_t len, + struct iommu_iotlb_gather *iotlb_gather) + { +- struct pt_iommu *iommu_table = +- container_of(domain, struct pt_iommu, domain); + struct pt_unmap_args unmap = { .free_list = IOMMU_PAGES_LIST_INIT( + unmap.free_list) }; +- pt_vaddr_t len = pgsize * pgcount; + struct pt_range range; + int ret; + +@@ -1062,7 +1040,6 @@ size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova, + + return unmap.unmapped; + } +-EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(unmap_pages), "GENERIC_PT_IOMMU"); + + static void NS(get_info)(struct pt_iommu *iommu_table, + struct pt_iommu_info *info) +@@ -1110,6 +1087,7 @@ static void NS(deinit)(struct pt_iommu *iommu_table) + } + + static const struct pt_iommu_ops NS(ops) = { ++ .unmap_range = NS(unmap_range), + #if IS_ENABLED(CONFIG_IOMMUFD_DRIVER) && defined(pt_entry_is_write_dirty) && \ + IS_ENABLED(CONFIG_IOMMUFD_TEST) && defined(pt_entry_make_write_dirty) + .set_dirty = NS(set_dirty), +@@ -1172,6 +1150,7 @@ static int pt_iommu_init_domain(struct pt_iommu *iommu_table, + + domain->type = __IOMMU_DOMAIN_PAGING; + domain->pgsize_bitmap = info.pgsize_bitmap; ++ domain->is_iommupt = true; + + if (pt_feature(common, PT_FEAT_DYNAMIC_TOP)) + range = _pt_top_range(common, +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index ef08c2c4ec95b..04b1c0f358b05 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include "dma-iommu.h" + #include "iommu-priv.h" +@@ -2710,13 +2711,12 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, + } + EXPORT_SYMBOL_GPL(iommu_map); + +-static size_t __iommu_unmap(struct iommu_domain *domain, +- unsigned long iova, size_t size, +- struct iommu_iotlb_gather *iotlb_gather) ++static size_t ++__iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, ++ size_t size, struct iommu_iotlb_gather *iotlb_gather) + { + const struct iommu_domain_ops *ops = domain->ops; + size_t unmapped_page, unmapped = 0; +- unsigned long orig_iova = iova; + unsigned int min_pagesz; + + if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) +@@ -2768,8 +2768,23 @@ static size_t __iommu_unmap(struct iommu_domain *domain, + unmapped += unmapped_page; + } + +- trace_unmap(orig_iova, size, unmapped); +- iommu_debug_unmap_end(domain, orig_iova, size, unmapped); ++ return unmapped; ++} ++ ++static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova, ++ size_t size, ++ struct iommu_iotlb_gather *iotlb_gather) ++{ ++ struct pt_iommu *pt = iommupt_from_domain(domain); ++ size_t unmapped; ++ ++ if (pt) ++ unmapped = pt->ops->unmap_range(pt, iova, size, iotlb_gather); ++ else ++ unmapped = __iommu_unmap_domain_pgtbl(domain, iova, size, ++ iotlb_gather); ++ trace_unmap(iova, size, unmapped); ++ iommu_debug_unmap_end(domain, iova, size, unmapped); + return unmapped; + } + +diff --git a/include/linux/generic_pt/iommu.h b/include/linux/generic_pt/iommu.h +index 9eefbb74efd08..f094f8f44e4e8 100644 +--- a/include/linux/generic_pt/iommu.h ++++ b/include/linux/generic_pt/iommu.h +@@ -66,6 +66,13 @@ struct pt_iommu { + struct device *iommu_device; + }; + ++static inline struct pt_iommu *iommupt_from_domain(struct iommu_domain *domain) ++{ ++ if (!IS_ENABLED(CONFIG_IOMMU_PT) || !domain->is_iommupt) ++ return NULL; ++ return container_of(domain, struct pt_iommu, domain); ++} ++ + /** + * struct pt_iommu_info - Details about the IOMMU page table + * +@@ -80,6 +87,29 @@ struct pt_iommu_info { + }; + + struct pt_iommu_ops { ++ /** ++ * @unmap_range: Make a range of IOVA empty/not present ++ * @iommu_table: Table to manipulate ++ * @iova: IO virtual address to start ++ * @len: Length of the range starting from @iova ++ * @iotlb_gather: Gather struct that must be flushed on return ++ * ++ * unmap_range() will remove a translation created by map_range(). It ++ * cannot subdivide a mapping created by map_range(), so it should be ++ * called with IOVA ranges that match those passed to map_pages. The ++ * IOVA range can aggregate contiguous map_range() calls so long as no ++ * individual range is split. ++ * ++ * Context: The caller must hold a write range lock that includes ++ * the whole range. ++ * ++ * Returns: Number of bytes of VA unmapped. iova + res will be the ++ * point unmapping stopped. ++ */ ++ size_t (*unmap_range)(struct pt_iommu *iommu_table, dma_addr_t iova, ++ dma_addr_t len, ++ struct iommu_iotlb_gather *iotlb_gather); ++ + /** + * @set_dirty: Make the iova write dirty + * @iommu_table: Table to manipulate +@@ -198,10 +228,6 @@ struct pt_iommu_cfg { + unsigned long iova, phys_addr_t paddr, \ + size_t pgsize, size_t pgcount, \ + int prot, gfp_t gfp, size_t *mapped); \ +- size_t pt_iommu_##fmt##_unmap_pages( \ +- struct iommu_domain *domain, unsigned long iova, \ +- size_t pgsize, size_t pgcount, \ +- struct iommu_iotlb_gather *iotlb_gather); \ + int pt_iommu_##fmt##_read_and_clear_dirty( \ + struct iommu_domain *domain, unsigned long iova, size_t size, \ + unsigned long flags, struct iommu_dirty_bitmap *dirty); \ +@@ -223,8 +249,7 @@ struct pt_iommu_cfg { + */ + #define IOMMU_PT_DOMAIN_OPS(fmt) \ + .iova_to_phys = &pt_iommu_##fmt##_iova_to_phys, \ +- .map_pages = &pt_iommu_##fmt##_map_pages, \ +- .unmap_pages = &pt_iommu_##fmt##_unmap_pages ++ .map_pages = &pt_iommu_##fmt##_map_pages + #define IOMMU_PT_DIRTY_OPS(fmt) \ + .read_and_clear_dirty = &pt_iommu_##fmt##_read_and_clear_dirty + +diff --git a/include/linux/iommu.h b/include/linux/iommu.h +index 555597b54083c..563d0f104114b 100644 +--- a/include/linux/iommu.h ++++ b/include/linux/iommu.h +@@ -223,6 +223,7 @@ enum iommu_domain_cookie_type { + struct iommu_domain { + unsigned type; + enum iommu_domain_cookie_type cookie_type; ++ bool is_iommupt; + const struct iommu_domain_ops *ops; + const struct iommu_dirty_ops *dirty_ops; + const struct iommu_ops *owner; /* Whose domain_alloc we came from */ +-- +2.53.0 + diff --git a/queue-7.0/iommupt-fix-the-end_index-calculation-in-__map_range.patch b/queue-7.0/iommupt-fix-the-end_index-calculation-in-__map_range.patch new file mode 100644 index 0000000000..5ff68c81dc --- /dev/null +++ b/queue-7.0/iommupt-fix-the-end_index-calculation-in-__map_range.patch @@ -0,0 +1,88 @@ +From 86f893482f55274ac6dd93523945ae1d168f3faa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:46:17 -0300 +Subject: iommupt: Fix the end_index calculation in __map_range_leaf() + +From: Jason Gunthorpe + +[ Upstream commit 58829512ad461af8f35941069c209941e3a97b65 ] + +Sashiko noticed a mismatch of units in this math: num_leaves is +actually the number of leaf *entries* (so a 16-item contiguous leaf +is one num_leaves), while index is in items. The mismatch in maths +causes __map_range_leaf() to exit early instead of efficiently +filling a larger range of contiguous PTEs. + +The early exit is caught by the functions above and then +__map_range_leaf() is re-invoked, so there is no functional issue. + +Correct the misuse of units by adjusting num_leaves with the leaf +size and avoid the performance cost of looping externally. + +There are also some mismatched types for num_leaves; simplify +things to remove the duplicated calculations. + +Fixes: d6c65b0fd621 ("iommupt: Avoid rewalking during map") +Signed-off-by: Jason Gunthorpe +Reviewed-by: Samiullah Khawaja +Reviewd-by: Pranjal Shrivastava +Tested-by: Josua Mayer +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + drivers/iommu/generic_pt/iommu_pt.h | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h +index 4be33c45bedc9..55faad4b9dc75 100644 +--- a/drivers/iommu/generic_pt/iommu_pt.h ++++ b/drivers/iommu/generic_pt/iommu_pt.h +@@ -523,10 +523,12 @@ static int __map_range_leaf(struct pt_range *range, void *arg, + struct pt_state pts = pt_init(range, level, table); + struct pt_iommu_map_args *map = arg; + unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2; ++ unsigned int leaves_avail; + unsigned int start_index; + pt_oaddr_t oa = map->oa; +- unsigned int num_leaves; ++ pt_vaddr_t num_leaves; + unsigned int orig_end; ++ unsigned int step_lg2; + pt_vaddr_t last_va; + unsigned int step; + bool need_contig; +@@ -535,21 +537,25 @@ static int __map_range_leaf(struct pt_range *range, void *arg, + PT_WARN_ON(map->leaf_level != level); + PT_WARN_ON(!pt_can_have_leaf(&pts)); + +- step = log2_to_int_t(unsigned int, +- leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts)); +- need_contig = leaf_pgsize_lg2 != pt_table_item_lg2sz(&pts); ++ step_lg2 = leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts); ++ step = log2_to_int_t(unsigned int, step_lg2); ++ need_contig = step_lg2 != 0; + + _pt_iter_first(&pts); + start_index = pts.index; + orig_end = pts.end_index; +- if (pts.index + map->num_leaves < pts.end_index) { ++ leaves_avail = ++ log2_div_t(unsigned int, pts.end_index - pts.index, step_lg2); ++ if (map->num_leaves <= leaves_avail) { + /* Need to stop in the middle of the table to change sizes */ +- pts.end_index = pts.index + map->num_leaves; ++ pts.end_index = pts.index + log2_mul(map->num_leaves, step_lg2); + num_leaves = 0; + } else { +- num_leaves = map->num_leaves - (pts.end_index - pts.index); ++ num_leaves = map->num_leaves - leaves_avail; + } + ++ PT_WARN_ON( ++ log2_mod_t(unsigned int, pts.end_index - pts.index, step_lg2)); + do { + pts.type = pt_load_entry_raw(&pts); + if (pts.type != PT_ENTRY_EMPTY || need_contig) { +-- +2.53.0 + diff --git a/queue-7.0/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch b/queue-7.0/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch new file mode 100644 index 0000000000..27e29aaddb --- /dev/null +++ b/queue-7.0/irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch @@ -0,0 +1,68 @@ +From 80ac00ad332c79940031c29ab8e98a01bd8a8c04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Mar 2026 15:32:29 +0800 +Subject: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jiayuan Chen + +[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] + +On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via +run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. + +After irq_work_single() clears BUSY via atomic_cmpxchg(), it still +dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). + +An irq_work_sync() caller on another CPU that enters after BUSY is cleared +can observe BUSY==0 immediately, return, and free the work before those +accesses complete — causing a use-after-free. + +Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire +irq_work_single() execution is within an RCU read-side critical +section. Then add synchronize_rcu() in irq_work_sync() after +rcuwait_wait_event() to ensure the caller waits for the RCU grace period +before returning, preventing premature frees. + +Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") +Suggested-by: Sebastian Andrzej Siewior +Suggested-by: Steven Rostedt +Signed-off-by: Jiayuan Chen +Signed-off-by: Thomas Gleixner +Reviewed-by: Sebastian Andrzej Siewior +Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev +Signed-off-by: Sasha Levin +--- + kernel/irq_work.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/kernel/irq_work.c b/kernel/irq_work.c +index 73f7e1fd4ab4d..bf411656c3160 100644 +--- a/kernel/irq_work.c ++++ b/kernel/irq_work.c +@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) + !arch_irq_work_has_interrupt()) { + rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), + TASK_UNINTERRUPTIBLE); ++ /* ++ * Ensure irq_work_single() does not access @work ++ * after removing IRQ_WORK_BUSY. It is always ++ * accessed within a RCU-read section. ++ */ ++ synchronize_rcu(); + return; + } + +@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); + + static void run_irq_workd(unsigned int cpu) + { ++ guard(rcu)(); + irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + +-- +2.53.0 + diff --git a/queue-7.0/irqchip-ath79-cpu-remove-unused-function.patch b/queue-7.0/irqchip-ath79-cpu-remove-unused-function.patch new file mode 100644 index 0000000000..b00ea165d6 --- /dev/null +++ b/queue-7.0/irqchip-ath79-cpu-remove-unused-function.patch @@ -0,0 +1,46 @@ +From 9f78ee7471d1b0933567dbb722012ff11147f6b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 01:55:22 -0700 +Subject: irqchip/ath79-cpu: Remove unused function + +From: Rosen Penev + +[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] + +ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a +while back. + +Remove it to get rid of a missing prototype warning, reported by the kernel test +robot. + +[ tglx: Fix the subject prefix. Sigh ... ] + +Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") +Reported-by: kernel test robot +Signed-off-by: Rosen Penev +Signed-off-by: Thomas Gleixner +Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com +Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c +index 923e4bba37767..9b7273a7f8ced 100644 +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} +-- +2.53.0 + diff --git a/queue-7.0/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch b/queue-7.0/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch new file mode 100644 index 0000000000..9e2eee0de0 --- /dev/null +++ b/queue-7.0/kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch @@ -0,0 +1,51 @@ +From 07f1841f0714057cb66047375a354b66e00b64d8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 23:58:45 +0200 +Subject: kbuild: pacman-pkg: make "rc" releases adhere to pacman versioning + scheme +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Viktor Jägersküpper + +[ Upstream commit 202550713128da20d9381d6d2dc0f6b73839f434 ] + +The package versioning scheme does not enable smooth upgrades from "rc" +releases to the corresponding stable releases (e.g. 7.0.0-rc7 -> 7.0.0) +because pacman considers that a downgrade due to the underscore in +pkgver (e.g. 7.0.0_rc7), see e.g. vercmp(8) for an explanation of the +package version comparison used by pacman. Package versions which are +derived from said releases (e.g. built from git revisions) are +similarly affected. Fix this by modifying pkgver in order to remove the +hyphen from kernel versions containing "-rcN", where N is a +non-negative integer. + +Acked-by: Thomas Weißschuh +Signed-off-by: Viktor Jägersküpper +Reviewed-by: Nathan Chancellor +Tested-by: Nathan Chancellor +Link: https://patch.msgid.link/20260515215913.92481-1-viktor_jaegerskuepper@freenet.de +Fixes: c8578539deba ("kbuild: add script and target to generate pacman package") +Signed-off-by: Nicolas Schier +Signed-off-by: Sasha Levin +--- + scripts/package/PKGBUILD | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD +index 452374d63c244..1213c8e04671e 100644 +--- a/scripts/package/PKGBUILD ++++ b/scripts/package/PKGBUILD +@@ -10,7 +10,7 @@ for pkg in $_extrapackages; do + pkgname+=("${pkgbase}-${pkg}") + done + +-pkgver="${KERNELRELEASE//-/_}" ++pkgver="$(echo "${KERNELRELEASE}" | sed 's/-\(rc[0-9]\+\)/\1/;s/-/_/g')" + # The PKGBUILD is evaluated multiple times. + # Running scripts/build-version from here would introduce inconsistencies. + pkgrel="${KBUILD_REVISION}" +-- +2.53.0 + diff --git a/queue-7.0/kho-skip-kho-for-crash-kernel.patch b/queue-7.0/kho-skip-kho-for-crash-kernel.patch new file mode 100644 index 0000000000..403db4e108 --- /dev/null +++ b/queue-7.0/kho-skip-kho-for-crash-kernel.patch @@ -0,0 +1,60 @@ +From 7b4daacd10ed0131a496dc46863df33c4b5b2074 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 01:16:05 +0000 +Subject: kho: skip KHO for crash kernel + +From: Evangelos Petrongonas + +[ Upstream commit a6715d7ec472a476db17787697a4abda62962284 ] + +kho_fill_kimage() unconditionally populates the kimage with KHO +metadata for every kexec image type. When the image is a crash kernel, +this can be problematic as the crash kernel can run in a small reserved +region and the KHO scratch areas can sit outside it. +The crash kernel then faults during kho_memory_init() when it +tries phys_to_virt() on the KHO FDT address: + + Unable to handle kernel paging request at virtual address xxxxxxxx + ... + fdt_offset_ptr+... + fdt_check_node_offset_+... + fdt_first_property_offset+... + fdt_get_property_namelen_+... + fdt_getprop+... + kho_memory_init+... + mm_core_init+... + start_kernel+... + +kho_locate_mem_hole() already skips KHO logic for KEXEC_TYPE_CRASH +images, but kho_fill_kimage() was missing the same guard. As +kho_fill_kimage() is the single point that populates image->kho.fdt +and image->kho.scratch, fixing it here is sufficient for both arm64 +and x86 as the FDT and boot_params path are bailing out when these +fields are unset. + +Fixes: d7255959b69a ("kho: allow kexec load before KHO finalization") +Signed-off-by: Evangelos Petrongonas +Reviewed-by: Mike Rapoport (Microsoft) +Link: https://patch.msgid.link/20260410011609.1103-1-epetron@amazon.de +Signed-off-by: Mike Rapoport (Microsoft) +Signed-off-by: Sasha Levin +--- + kernel/liveupdate/kexec_handover.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c +index 479c42e08b74a..d8893f2adce8a 100644 +--- a/kernel/liveupdate/kexec_handover.c ++++ b/kernel/liveupdate/kexec_handover.c +@@ -1556,7 +1556,7 @@ int kho_fill_kimage(struct kimage *image) + int err = 0; + struct kexec_buf scratch; + +- if (!kho_enable) ++ if (!kho_enable || image->type == KEXEC_TYPE_CRASH) + return 0; + + image->kho.fdt = virt_to_phys(kho_out.fdt); +-- +2.53.0 + diff --git a/queue-7.0/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch b/queue-7.0/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch new file mode 100644 index 0000000000..08c844c87f --- /dev/null +++ b/queue-7.0/kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch @@ -0,0 +1,103 @@ +From fced19eb2195cf2aed36b9b3b37939528340ed93 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() + +From: Jianpeng Chang + +[ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ] + +When kprobe_add_area_blacklist() iterates through a section like +.kprobes.text, the start address may not correspond to a named symbol. +On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by +commit baaf553d3bc3 ("arm64: Implement +HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag +-fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry +point for ftrace call_ops. These pre-function NOPs sit at the section base +address, before the first named function symbol. The compiler emits a $x +mapping symbol at offset 0x00 to mark the start of code, but +find_kallsyms_symbol() ignores mapping symbols. + +Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no +pre-function NOPs are inserted, the first function starts at offset +0x00, and the bug does not trigger. + +This only affects modules that have a .kprobes.text section (i.e. those +using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead +(like kretprobe_example.ko) blacklist exact function addresses via the +_kprobe_blacklist section and are not affected. + +For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, +the .kprobes.text section layout is: + + offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) + offset 0x08: handler_post (64 bytes) + offset 0x50: handler_pre (68 bytes) + +kprobe_add_area_blacklist() starts iterating from the section base +address (offset 0x00), which only has the $x mapping symbol. +kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() +for this address, which goes through: + + kallsyms_lookup_size_offset() + -> module_address_lookup() + -> find_kallsyms_symbol() + +find_kallsyms_symbol() scans all module symbols to find the closest +preceding symbol. + +Since no named text symbol exists at offset 0x00, +find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol +whose address is in the temporary image) as the "best" match. The +computed "size" = next_text_symbol - modinfo_symbol spans across +these two unrelated memory regions, creating a blacklist entry with +a bogus range of tens of terabytes. + +Whether this causes a visible failure depends on address randomization, +here is what happens on Raspberry Pi 4/5: + + - On RPi5, the bogus size was ~35 TB. start + size stayed within + 64-bit range, so the blacklist entry covered the entire kernel + text. register_kprobe() in the module's own init function failed + with -EINVAL. + + - On RPi4, the bogus size was ~75 TB. start + size overflowed + 64 bits and wrapped to a small address near zero. The range + check (addr >= start && addr < end) then failed because end + wrapped around, so the bogus entry was accidentally harmless + and kprobes worked by luck. + +The same bug exists on both machines, but randomization determines whether +the integer overflow masks it or not. + +Fix this by adding notrace to the __kprobes macro. Functions in +.kprobes.text are kprobe infrastructure handlers that should never be +traced by ftrace. With notrace, the compiler stops inserting them and the +non-symbol gap at the section start disappears entirely. + +Link: https://lore.kernel.org/all/20260506012706.2785785-1-jianpeng.chang.cn@windriver.com/ + +Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") +Signed-off-by: Jianpeng Chang +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + include/asm-generic/kprobes.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h +index 060eab094e5a2..5290a2b2e15a0 100644 +--- a/include/asm-generic/kprobes.h ++++ b/include/asm-generic/kprobes.h +@@ -14,7 +14,7 @@ static unsigned long __used \ + _kbl_addr_##fname = (unsigned long)fname; + # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) + /* Use this to forbid a kprobes attach on very low level functions */ +-# define __kprobes __section(".kprobes.text") ++# define __kprobes notrace __section(".kprobes.text") + # define nokprobe_inline __always_inline + #else + # define NOKPROBE_SYMBOL(fname) +-- +2.53.0 + diff --git a/queue-7.0/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch b/queue-7.0/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch new file mode 100644 index 0000000000..910baf5549 --- /dev/null +++ b/queue-7.0/ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch @@ -0,0 +1,63 @@ +From a94dd74efc8a0afe4f00b5320b257d2a06aa4778 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 23:27:19 +0900 +Subject: ksmbd: fix durable reconnect error path file lifetime + +From: Junyi Liu + +[ Upstream commit 3515503322f4819277091839eed46b695096aca5 ] + +After a durable reconnect succeeds, ksmbd_reopen_durable_fd() republishes +the same ksmbd_file into the session volatile-id table. If smb2_open() +then takes a later error path, cleanup first calls ksmbd_fd_put(work, fp) +and then unconditionally calls ksmbd_put_durable_fd(dh_info.fp). + +In this case fp and dh_info.fp are the same object. The first put drops the +reconnect lookup reference, but the final durable put can run +__ksmbd_close_fd(NULL, fp). Because the final close is not session-aware, +it can free the file object without removing the volatile-id entry that was +just published into the session table. + +Use the session-aware put for the final reconnect drop when the reconnect +had already succeeded and the error path is cleaning up the republished +file. Earlier reconnect failures, before fp is assigned to dh_info.fp, keep +using the durable-only put path. + +Fixes: 1baff47b81f9 ("ksmbd: fix use-after-free in smb2_open during durable reconnect") +Signed-off-by: Junyi Liu +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index c3c7688f0fa80..3a8a739c025fb 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -3803,8 +3803,19 @@ int smb2_open(struct ksmbd_work *work) + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + +- if (dh_info.reconnected) +- ksmbd_put_durable_fd(dh_info.fp); ++ if (dh_info.reconnected) { ++ /* ++ * If reconnect succeeded, fp was republished in the ++ * session file table. On a later error, ksmbd_fd_put() ++ * above drops the session reference; drop the durable ++ * lookup reference through the same session-aware path so ++ * final close removes the volatile id before freeing fp. ++ */ ++ if (rc && fp == dh_info.fp) ++ ksmbd_fd_put(work, dh_info.fp); ++ else ++ ksmbd_put_durable_fd(dh_info.fp); ++ } + + kfree(name); + kfree(lc); +-- +2.53.0 + diff --git a/queue-7.0/kunit-config-enable-kunit_debugfs-by-default.patch b/queue-7.0/kunit-config-enable-kunit_debugfs-by-default.patch new file mode 100644 index 0000000000..0bff5c0432 --- /dev/null +++ b/queue-7.0/kunit-config-enable-kunit_debugfs-by-default.patch @@ -0,0 +1,42 @@ +From ffa025edecf953723bdd35dd2ff9ae40750276cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:53 +0800 +Subject: kunit: config: Enable KUNIT_DEBUGFS by default + +From: David Gow + +[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] + +The KUNIT_DEBUGFS option is currently enabled based on the value of +KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of +enabled tests, so just enable it by default anyway. In particular, this +shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite +confusing. + +Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net +Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index 498cc51e493dc..f80ca3aeedb05 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -16,8 +16,8 @@ menuconfig KUNIT + if KUNIT + + config KUNIT_DEBUGFS +- bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS +- default KUNIT_ALL_TESTS ++ bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ default y + help + Enable debugfs representation for kunit. Currently this consists + of /sys/kernel/debug/kunit//results files for each +-- +2.53.0 + diff --git a/queue-7.0/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch b/queue-7.0/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch new file mode 100644 index 0000000000..8689f0144a --- /dev/null +++ b/queue-7.0/kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch @@ -0,0 +1,36 @@ +From ee50d40761d2e2f8ff34134b9ddc6c686aee439a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Apr 2026 11:41:54 +0800 +Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS + +From: David Gow + +[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] + +CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should +depend on CONFIG_DEBUG_FS. + +Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net +Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") +Signed-off-by: David Gow +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + lib/kunit/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig +index f80ca3aeedb05..94ff8e4089bfb 100644 +--- a/lib/kunit/Kconfig ++++ b/lib/kunit/Kconfig +@@ -17,6 +17,7 @@ if KUNIT + + config KUNIT_DEBUGFS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" ++ depends on DEBUG_FS + default y + help + Enable debugfs representation for kunit. Currently this consists +-- +2.53.0 + diff --git a/queue-7.0/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch b/queue-7.0/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch new file mode 100644 index 0000000000..7d26c441ef --- /dev/null +++ b/queue-7.0/loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch @@ -0,0 +1,76 @@ +From 09d12455050c5560c125b9e19d6276c9072850e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 15:05:07 +0800 +Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions + +From: Tiezhu Yang + +[ Upstream commit 1c856e158fd34ef2c4475a81c1dc386329989938 ] + +KPROBE_HIT_SS and KPROBE_REENTER are two types of fatal recursions that +can not be safely recovered in kprobes. + +KPROBE_HIT_SS means that a kprobe is hit during single-stepping. At +this point, the architecture-specific single-step context is already +active. Nested single-stepping would corrupt the state, as the kprobe +control block (kcb) and hardware registers cannot safely store multiple +levels of stepping state. + +KPROBE_REENTER means that a third-level recursion occurs when a probe +is hit while the system is already handling a nested probe (second- +level). The kcb only provides a single slot (prev_kprobe) to backup the +state. When a third probe is hit, there is no more space to save the +state without corrupting the first-level backup. + +Kprobes work by replacing instructions with breakpoints. In order to +execute the original instruction and continue, it must be moved to a +temporary "single-step" slot. Since there is no backup space left to +set up this slot safely, the CPU would be forced to return to the same +original breakpoint address, triggering an endless loop. + +Currently, the code only prints a warning and returns. This leads to +an infinite re-entry loop as the CPU repeatedly hits the same trap and +a "stuck" CPU core because preemption was disabled at the start of the +handler and never re-enabled in this early return path. + +Fix the logic by: +1. Merging KPROBE_HIT_SS and KPROBE_REENTER cases, as both represent + fatal recursions that cannot be safely recovered. +2. Replacing WARN_ON_ONCE() with BUG() to terminate the system. This + aligns LoongArch with other architectures (x86, arm64, riscv) and + prevents stack overflow while providing diagnostic information. + +Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") +Signed-off-by: Tiezhu Yang +Signed-off-by: Huacai Chen +Signed-off-by: Sasha Levin +--- + arch/loongarch/kernel/kprobes.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c +index 04b5b05715cdc..1985ed30dd16f 100644 +--- a/arch/loongarch/kernel/kprobes.c ++++ b/arch/loongarch/kernel/kprobes.c +@@ -186,16 +186,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) + { + switch (kcb->kprobe_status) { +- case KPROBE_HIT_SS: + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs, kcb, 1); + break; ++ case KPROBE_HIT_SS: + case KPROBE_REENTER: + pr_warn("Failed to recover from reentered kprobes.\n"); + dump_kprobe(p); +- WARN_ON_ONCE(1); ++ BUG(); + break; + default: + WARN_ON(1); +-- +2.53.0 + diff --git a/queue-7.0/mm-memfd_luo-report-error-when-restoring-a-folio-fai.patch b/queue-7.0/mm-memfd_luo-report-error-when-restoring-a-folio-fai.patch new file mode 100644 index 0000000000..e5d6eb9b3c --- /dev/null +++ b/queue-7.0/mm-memfd_luo-report-error-when-restoring-a-folio-fai.patch @@ -0,0 +1,48 @@ +From 6dab2eaeb20e7cb97a202b69a66ba9638e6ae438 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Apr 2026 06:23:00 +0100 +Subject: mm/memfd_luo: report error when restoring a folio fails mid-loop + +From: David Carlier + +[ Upstream commit 0fb1daf0b78d0e23b63b6b65de56d4a3fd83bc14 ] + +memfd_luo_retrieve_folios() initialises err to -EIO, but the per-iteration +calls to mem_cgroup_charge(), shmem_add_to_page_cache() and +shmem_inode_acct_blocks() reuse and overwrite err. Once any iteration +completes successfully, err becomes zero. + +If a later iteration's kho_restore_folio() returns NULL, the failure path +jumps to put_folios without resetting err, so the function returns 0. +The caller memfd_luo_retrieve() then takes the success path, sets +args->file and reports the restore as successful, leaving userspace with +a partially populated memfd and no indication that anything went wrong. + +Set err to -EIO in the kho_restore_folio() failure branch so the error +is propagated to the caller. + +Signed-off-by: David Carlier +Reviewed-by: Pratyush Yadav +Fixes: b3749f174d68 ("mm: memfd_luo: allow preserving memfd") +Link: https://patch.msgid.link/20260415052300.362539-1-devnexen@gmail.com +Signed-off-by: Mike Rapoport (Microsoft) +Signed-off-by: Sasha Levin +--- + mm/memfd_luo.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c +index cfd665a5b7874..bb5f601418032 100644 +--- a/mm/memfd_luo.c ++++ b/mm/memfd_luo.c +@@ -412,6 +412,7 @@ static int memfd_luo_retrieve_folios(struct file *file, + if (!folio) { + pr_err("Unable to restore folio at physical address: %llx\n", + phys); ++ err = -EIO; + goto put_folios; + } + index = pfolio->index; +-- +2.53.0 + diff --git a/queue-7.0/net-ag71xx-check-error-for-platform_get_irq.patch b/queue-7.0/net-ag71xx-check-error-for-platform_get_irq.patch new file mode 100644 index 0000000000..180b6d073f --- /dev/null +++ b/queue-7.0/net-ag71xx-check-error-for-platform_get_irq.patch @@ -0,0 +1,38 @@ +From dcd20b6314bf1422470355f9a569b1aeed0aa285 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 14:26:16 -0700 +Subject: net: ag71xx: check error for platform_get_irq + +From: Rosen Penev + +[ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] + +Complete error handling for a failed platform_get_irq() call + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Rosen Penev +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index a5ab994741790..4e4794c4dfdce 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1856,6 +1856,9 @@ static int ag71xx_probe(struct platform_device *pdev) + ag71xx_int_disable(ag, AG71XX_INT_POLL); + + ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq < 0) ++ return ndev->irq; ++ + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), ndev); + if (err) { +-- +2.53.0 + diff --git a/queue-7.0/net-airoha-disable-gdm2-forwarding-before-configurin.patch b/queue-7.0/net-airoha-disable-gdm2-forwarding-before-configurin.patch new file mode 100644 index 0000000000..ba1e69a45f --- /dev/null +++ b/queue-7.0/net-airoha-disable-gdm2-forwarding-before-configurin.patch @@ -0,0 +1,54 @@ +From 8aaf78786d0fb00cea8809bb199432c0625ef8e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 15:12:02 +0200 +Subject: net: airoha: Disable GDM2 forwarding before configuring GDM2 loopback + +From: Lorenzo Bianconi + +[ Upstream commit 985d4a55e64e43bd86eeb896b81ceba453301989 ] + +Hw design requires to disable GDM2 forwarding before configuring GDM2 +loopback in airoha_set_gdm2_loopback routine. + +Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") +Tested-by: Madhur Agrawal +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c +index 83882a8953d25..13f7433592866 100644 +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1781,11 +1781,8 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + u32 val, pse_port, chan; + int src_port; + +- /* Forward the traffic to the proper GDM port */ +- pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 +- : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), +- pse_port); ++ FE_PSE_PORT_DROP); + airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + GDM_STRIP_CRC_MASK); + +@@ -1803,6 +1800,11 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); ++ /* Forward the traffic to the proper GDM port */ ++ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 ++ : FE_PSE_PORT_GDM4; ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ pse_port); + + /* Disable VIP and IFC for GDM2 */ + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); +-- +2.53.0 + diff --git a/queue-7.0/net-airoha-fix-npu-rx-dma-descriptor-bits.patch b/queue-7.0/net-airoha-fix-npu-rx-dma-descriptor-bits.patch new file mode 100644 index 0000000000..acff34acaa --- /dev/null +++ b/queue-7.0/net-airoha-fix-npu-rx-dma-descriptor-bits.patch @@ -0,0 +1,52 @@ +From 786508ca50476dd7bd45b77a1d3461e91e335c5e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 15:44:57 +0200 +Subject: net: airoha: Fix NPU RX DMA descriptor bits + +From: Christian Marangi + +[ Upstream commit 0cb5a74faa3bdcfa3b18735d554e12c0f615e35d ] + +In an internal review from Airoha, it was notice that the RX DMA descriptor +bits and mask are wrong. These values probably refer to an old NPU firmware +never published. The previous value works correctly but it was reported +that in some specific condition in mixed scenario with both Ethernet and +WiFi offload it's possible that RX DMA descriptor signal wrong value with +the problem to the RX ring or packets getting dropped. + +To handle these specific scenario, apply the new suggested bits mask from +Airoha. + +Correct functionality of both AN7581 NPU and MT7996 variant were verified +and confirmed working. + +Fixes: a7fc8c641cab ("net: airoha: Fix npu rx DMA definitions") +Signed-off-by: Christian Marangi +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260518134530.3683-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/linux/soc/airoha/airoha_offload.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/soc/airoha/airoha_offload.h b/include/linux/soc/airoha/airoha_offload.h +index d01ef4a6b3d7c..7589fccfeef6d 100644 +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -71,9 +71,9 @@ static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, + #define NPU_RX1_DESC_NUM 512 + + /* CTRL */ +-#define NPU_RX_DMA_DESC_LAST_MASK BIT(27) +-#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(26, 14) +-#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(13, 1) ++#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) ++#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) ++#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) + #define NPU_RX_DMA_DESC_DONE_MASK BIT(0) + /* INFO */ + #define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 29) +-- +2.53.0 + diff --git a/queue-7.0/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch b/queue-7.0/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch new file mode 100644 index 0000000000..14d044fde2 --- /dev/null +++ b/queue-7.0/net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch @@ -0,0 +1,97 @@ +From 215ae3bd8a4ab83a7bd76fc4d931945bcd8742b1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:21 +0100 +Subject: net: dsa: mt7530: fix FDB entries not aging out with short timeout + +From: Daniel Golle + +[ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] + +The DSA forwarding selftests bridge_vlan_aware.sh and +bridge_vlan_unaware.sh configure the bridge with ageing_time set to +LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run +learning_test() in lib.sh, which expects a learned FDB entry to be +removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry +persisted past the deadline and the "Found FDB record when should +not" assertion failed. + +With msecs=10000, the algorithm in mt7530_set_ageing_time() finds +AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the +search from tmp_age_count=0). The per-entry aging counter is +initialized to AGE_CNT when a MAC address is learned, so with +AGE_CNT=0 new entries start with a counter value of 0, which the +hardware treats as "already aged" and never removes, effectively +disabling aging. + +Fix this by starting the search from tmp_age_count=1 to ensure +entries always have a non-zero initial aging counter. For a +10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: +the timer ticks every 5 seconds and entries are removed after 2 +ticks. + +Starting the search at AGE_CNT=1 raises the minimum representable +ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time +of 1 second would now make the loop fall through without setting +age_count and age_unit, leaving them uninitialized when written to +the MT7530_AAC hardware register. Set ds->ageing_time_min and +ds->ageing_time_max so the DSA core validates the range before the +callback is invoked, and drop the now-redundant range check from +mt7530_set_ageing_time(). + +Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index b9423389c2ef0..f90f9ea515d81 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -973,12 +973,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + unsigned int age_count; + unsigned int age_unit; + +- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ +- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) +- return -ERANGE; +- +- /* iterate through all possible age_count to find the closest pair */ +- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. ++ * The DSA core has already validated the range using ++ * ds->ageing_time_min and ds->ageing_time_max. ++ * ++ * Iterate through all possible age_count values to find the closest ++ * pair. Start from 1 because the per-entry aging counter is ++ * initialized to AGE_CNT and a value of 0 means the entry will ++ * never be aged out. ++ */ ++ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; + + if (tmp_age_unit <= AGE_UNIT_MAX) { +@@ -2378,6 +2382,8 @@ mt7530_setup(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -2567,6 +2573,8 @@ mt7531_setup_common(struct dsa_switch *ds) + + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; ++ ds->ageing_time_min = 2 * 1000; ++ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; + + mt753x_trap_frames(priv); + +-- +2.53.0 + diff --git a/queue-7.0/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch b/queue-7.0/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch new file mode 100644 index 0000000000..6bc07d3069 --- /dev/null +++ b/queue-7.0/net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch @@ -0,0 +1,97 @@ +From 8c85e18893f01d91fab1014f699df3b2d0e7f4af Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 15:04:35 +0100 +Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames + +From: Daniel Golle + +[ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] + +The BPC, RGAC1 and RGAC2 registers control the handling of link-local +frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are +correctly trapped to the CPU port, but the egress VLAN tag attribute was +set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any +VLAN tags from trapped frames before they reach the CPU. + +This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer +Delay Requests) to arrive at the CPU without their VLAN tag, so they +are delivered to the base network interface instead of the VLAN +sub-interface. The DSA local_termination selftest confirms this: all +link-local protocol tests on VLAN upper interfaces fail. + +Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) +so that the switch does not modify VLAN tags in trapped frames. This +way VLAN-tagged frames retain their original tag and are delivered to +the correct VLAN sub-interface, matching the behavior of non-trapped +frames which pass through without VLAN tag modification. + +Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") +Signed-off-by: Daniel Golle +Acked-by: Chester A. Unal +Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index f90f9ea515d81..cc269d16b75d1 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1250,37 +1250,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + static void + mt753x_trap_frames(struct mt7530_priv *priv) + { +- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them +- * VLAN-untagged. ++ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress ++ * them with the EG_TAG attribute set to disabled (system default) ++ * so that any VLAN tags in the frame are not modified by the ++ * switch egress VLAN tag processing. This preserves VLAN tags ++ * for reception on VLAN sub-interfaces. + */ + mt7530_rmw(priv, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, +- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | +- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, +- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | +- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + +- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress +- * them VLAN-untagged. ++ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and ++ * egress them with EG_TAG disabled. + */ + mt7530_rmw(priv, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, +- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | +- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); + } + +-- +2.53.0 + diff --git a/queue-7.0/net-enetc-fix-missing-error-code-when-pf-vf_state-al.patch b/queue-7.0/net-enetc-fix-missing-error-code-when-pf-vf_state-al.patch new file mode 100644 index 0000000000..4e5718e7ed --- /dev/null +++ b/queue-7.0/net-enetc-fix-missing-error-code-when-pf-vf_state-al.patch @@ -0,0 +1,46 @@ +From 5ac277a2582278ebdfaa7a3fe54a4631e0842134 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:44:14 +0800 +Subject: net: enetc: fix missing error code when pf->vf_state allocation fails + +From: Wei Fang + +[ Upstream commit 5027266dea471e140f93dd534845c9c4f43219a3 ] + +In enetc_pf_probe(), when the memory allocation for pf->vf_state fails, +the code jumps to the error handling label but the variable 'err' is not +assigned an appropriate error code beforehand. This causes the function +to return 0 (success) on an allocation failure path, misleading the +caller into thinking the probe succeeded. So set err to -ENOMEM before +jumping to the error handling label when the allocation for pf->vf_state +returns NULL. + +Fixes: e15c5506dd39 ("net: enetc: allocate vf_state during PF probes") +Signed-off-by: Wei Fang +Reviewed-by: Harshitha Ramamurthy +Link: https://patch.msgid.link/20260520064421.91569-3-wei.fang@nxp.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/freescale/enetc/enetc_pf.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +index a12fd54a475f6..ed8f7b3dc5260 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -960,8 +960,10 @@ static int enetc_pf_probe(struct pci_dev *pdev, + if (pf->total_vfs) { + pf->vf_state = kzalloc_objs(struct enetc_vf_state, + pf->total_vfs); +- if (!pf->vf_state) ++ if (!pf->vf_state) { ++ err = -ENOMEM; + goto err_alloc_vf_state; ++ } + } + + err = enetc_setup_mac_addresses(node, pf); +-- +2.53.0 + diff --git a/queue-7.0/net-ethernet-cortina-carry-over-frag-counter.patch b/queue-7.0/net-ethernet-cortina-carry-over-frag-counter.patch new file mode 100644 index 0000000000..08e7447ce6 --- /dev/null +++ b/queue-7.0/net-ethernet-cortina-carry-over-frag-counter.patch @@ -0,0 +1,118 @@ +From 2d20e4722cb9e67592057e79dbbbadff99a6cb1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:38 +0200 +Subject: net: ethernet: cortina: Carry over frag counter + +From: Linus Walleij + +[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] + +The gmac_rx() NAPI poll function assembles packets in an +SKB from a ring buffer. + +If the ring buffer gets completely emptied during a poll cycle, +we exit gmac_rx(), but the packet is not yet completely +assembled in the SKB, yet the fragment counter frag_nr is +reset to zero on the next invocation. + +Solve this by making the RX fragment counter a part of the +port struct, and carry it over between invocations. + +Reset the fragment counter only right after calling +napi_gro_frags(), on error (after calling napi_free_frags()) +or if stopping the port. + +Reset it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index e8d973b8fb0c3..ccd14a386e3b9 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -123,6 +123,7 @@ struct gemini_ethernet_port { + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; ++ unsigned int rx_frag_nr; + + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; +@@ -1444,6 +1445,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; +@@ -1457,7 +1459,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; +- int frag_nr = 0; + + spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); +@@ -1497,6 +1498,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + continue; + } +@@ -1507,6 +1509,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; ++ frag_nr = 0; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1541,6 +1544,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (word3.bits32 & EOF_BIT) { + napi_gro_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + --budget; + } + continue; +@@ -1549,6 +1553,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; ++ frag_nr = 0; + } + + if (mapping) +@@ -1558,6 +1563,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + } + + port->rx_skb = skb; ++ port->rx_frag_nr = frag_nr; + writew(r, ptr_reg); + return budget; + } +@@ -1886,6 +1892,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_stop_dma(port); + napi_disable(&port->napi); + port->rx_skb = NULL; ++ port->rx_frag_nr = 0; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-7.0/net-ethernet-cortina-drop-half-assembled-skb.patch b/queue-7.0/net-ethernet-cortina-drop-half-assembled-skb.patch new file mode 100644 index 0000000000..fda9e54040 --- /dev/null +++ b/queue-7.0/net-ethernet-cortina-drop-half-assembled-skb.patch @@ -0,0 +1,54 @@ +From afc26710ac5cd7d94901ab04910ebcaa42d001b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 23:52:17 +0200 +Subject: net: ethernet: cortina: Drop half-assembled SKB +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Andreas Haarmann-Thiemann + +[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] + +In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when +gmac_get_queue_page() returns NULL for the second page of a multi-page +fragment, the driver logs an error and continues — but does not free the +partially assembled skb that was being assembled via napi_build_skb() / +napi_get_frags(). + +Free the in-progress partially assembled skb via napi_free_frags() +and increase the number of dropped frames appropriately +and assign the skb pointer NULL to make sure it is not lingering +around, matching the pattern already used elsewhere in the driver. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Andreas Haarmann-Thiemann +Signed-off-by: Linus Walleij +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: ebd8ec2b309e ("net: ethernet: cortina: Carry over frag counter") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 57a25030f883c..e8d973b8fb0c3 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1493,6 +1493,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); + if (!gpage) { + dev_err(geth->dev, "could not find mapping\n"); ++ if (skb) { ++ napi_free_frags(&port->napi); ++ port->stats.rx_dropped++; ++ skb = NULL; ++ } + continue; + } + page = gpage->page; +-- +2.53.0 + diff --git a/queue-7.0/net-ethernet-cortina-make-rx-skb-per-port.patch b/queue-7.0/net-ethernet-cortina-make-rx-skb-per-port.patch new file mode 100644 index 0000000000..6d0e3269b0 --- /dev/null +++ b/queue-7.0/net-ethernet-cortina-make-rx-skb-per-port.patch @@ -0,0 +1,87 @@ +From f0a0f444746dcb338877887abbd07b01b4d566ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 May 2026 00:13:37 +0200 +Subject: net: ethernet: cortina: Make RX SKB per-port + +From: Linus Walleij + +[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] + +The SKB used to assemble packets from fragments in gmac_rx() +is static local, but the Gemini has two ethernet ports, meaning +there can be races between the ports on a bad day if a device +is using both. + +Make the RX SKB a per-port variable and carry it over between +invocations in the port struct instead. + +Zero the pointer once we call napi_gro_frags(), on error (after +calling napi_free_frags()) or if the port is stopped. + +Zero it in some place where not strictly necessary just to +emphasize what is going on. + +This was found by Sashiko during normal patch review. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org +Signed-off-by: Linus Walleij +Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cortina/gemini.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c +index 4824232f48907..57a25030f883c 100644 +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -122,6 +122,8 @@ struct gemini_ethernet_port { + struct napi_struct napi; + struct hrtimer rx_coalesce_timer; + unsigned int rx_coalesce_nsecs; ++ struct sk_buff *rx_skb; ++ + unsigned int freeq_refill; + struct gmac_txq txq[TX_QUEUE_NUM]; + unsigned int txq_order; +@@ -1442,10 +1444,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + unsigned short m = (1 << port->rxq_order) - 1; + struct gemini_ethernet *geth = port->geth; + void __iomem *ptr_reg = port->rxq_rwptr; ++ struct sk_buff *skb = port->rx_skb; + unsigned int frame_len, frag_len; + struct gmac_rxdesc *rx = NULL; + struct gmac_queue_page *gpage; +- static struct sk_buff *skb; + union gmac_rxdesc_0 word0; + union gmac_rxdesc_1 word1; + union gmac_rxdesc_3 word3; +@@ -1499,6 +1501,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; ++ skb = NULL; + } + + skb = gmac_skb_if_good_frame(port, word0, frame_len); +@@ -1549,6 +1552,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) + port->stats.rx_dropped++; + } + ++ port->rx_skb = skb; + writew(r, ptr_reg); + return budget; + } +@@ -1876,6 +1880,7 @@ static int gmac_stop(struct net_device *netdev) + gmac_disable_tx_rx(netdev); + gmac_stop_dma(port); + napi_disable(&port->napi); ++ port->rx_skb = NULL; + + gmac_enable_irq(netdev, 0); + gmac_cleanup_rxq(netdev); +-- +2.53.0 + diff --git a/queue-7.0/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch b/queue-7.0/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch new file mode 100644 index 0000000000..00479b44f9 --- /dev/null +++ b/queue-7.0/net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch @@ -0,0 +1,45 @@ +From a162b2d33fc671ff093e74e183b7f9511be5c031 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 19:37:28 -0700 +Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference + +From: Ethan Nelson-Moore + +[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] + +The legacy ARM board file for MACH_MX31ADS was removed in commit +c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference +to it remained in the cs89x0 driver. Drop this unused code. + +Signed-off-by: Ethan Nelson-Moore +Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") +Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/cirrus/cs89x0.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c +index fa5857923db4c..b4bfd6c174e78 100644 +--- a/drivers/net/ethernet/cirrus/cs89x0.c ++++ b/drivers/net/ethernet/cirrus/cs89x0.c +@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { + + static void __init reset_chip(struct net_device *dev) + { +-#if !defined(CONFIG_MACH_MX31ADS) + struct net_local *lp = netdev_priv(dev); + unsigned long reset_start_time; + +@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) + while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && + time_before(jiffies, reset_start_time + 2)) + ; +-#endif /* !CONFIG_MACH_MX31ADS */ + } + + /* This is the real probe routine. +-- +2.53.0 + diff --git a/queue-7.0/net-gro-don-t-merge-zcopy-skbs.patch b/queue-7.0/net-gro-don-t-merge-zcopy-skbs.patch new file mode 100644 index 0000000000..07ed02097b --- /dev/null +++ b/queue-7.0/net-gro-don-t-merge-zcopy-skbs.patch @@ -0,0 +1,48 @@ +From a0124f8f241371e893db34099c360106053059c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 22:44:42 +0200 +Subject: net: gro: don't merge zcopy skbs + +From: Sabrina Dubroca + +[ Upstream commit 4db79a322db8c97f7b73b8a347395ef4d685eb40 ] + +skb_gro_receive() can currently copy frags between the source and GRO +skb, without checking the zerocopy status, and in particular the +SKBFL_MANAGED_FRAG_REFS flag. + +When SKBFL_MANAGED_FRAG_REFS is set, the skb doesn't hold a reference +on the pages in shinfo->frags. Appending those frags to another skb's +frags without fixing up the page refcount can lead to UAF. + +When either the last skb in the GRO chain (the one we would append +frags to) or the source skb is zerocopy, don't merge the skbs. + +Fixes: 753f1ca4e1e5 ("net: introduce managed frags infrastructure") +Reported-by: Huzaifa Sidhpurwala +Signed-off-by: Sabrina Dubroca +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/gro.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/gro.c b/net/core/gro.c +index 9f8960789b2cf..a847539834679 100644 +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -109,6 +109,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) + if (p->pp_recycle != skb->pp_recycle) + return -ETOOMANYREFS; + ++ if (skb_zcopy(p) || skb_zcopy(skb)) ++ return -ETOOMANYREFS; ++ + if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) || + NAPI_GRO_CB(skb)->flush)) + return -E2BIG; +-- +2.53.0 + diff --git a/queue-7.0/net-lan966x-avoid-unregistering-netdev-on-register-f.patch b/queue-7.0/net-lan966x-avoid-unregistering-netdev-on-register-f.patch new file mode 100644 index 0000000000..de2941a2bd --- /dev/null +++ b/queue-7.0/net-lan966x-avoid-unregistering-netdev-on-register-f.patch @@ -0,0 +1,65 @@ +From d9e86f33159eb4d158f2376dfa8ffe20549e1a5a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 21:43:11 +0900 +Subject: net: lan966x: avoid unregistering netdev on register failure + +From: Myeonghun Pak + +[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] + +lan966x_probe_port() stores the newly allocated net_device in the +port before calling register_netdev(). If register_netdev() fails, +the probe error path calls lan966x_cleanup_ports(), which sees +port->dev and calls unregister_netdev() for a device that was never +registered. + +Destroy the phylink instance created for this port and clear port->dev +before returning the registration error. The common cleanup path now skips +ports without port->dev before reaching the registered netdev cleanup, so +it only handles ports that reached the registered-netdev lifetime. + +This also avoids treating an uninitialized FDMA netdev and the failed port +as a NULL == NULL match in the common cleanup path. + +Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +index 47752d3fde0b1..1179a6e127c52 100644 +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +@@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; +- if (!port) ++ if (!port || !port->dev) + continue; + +- if (port->dev) +- unregister_netdev(port->dev); ++ unregister_netdev(port->dev); + + lan966x_xdp_port_deinit(port); + if (lan966x->fdma && lan966x->fdma_ndev == port->dev) +@@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, + err = register_netdev(dev); + if (err) { + dev_err(lan966x->dev, "register_netdev failed\n"); ++ phylink_destroy(phylink); ++ port->phylink = NULL; ++ port->dev = NULL; + return err; + } + +-- +2.53.0 + diff --git a/queue-7.0/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch b/queue-7.0/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch new file mode 100644 index 0000000000..4afe0e77ad --- /dev/null +++ b/queue-7.0/net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch @@ -0,0 +1,95 @@ +From 23371111c2dc3dbe488ce85c264563c34608167d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 12:41:51 -0700 +Subject: net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer + +From: Erni Sri Satya Vennela + +[ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] + +In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from +DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() +re-reads the same field from the same DMA buffer for test_bit() and +pointer arithmetic. + +DMA-coherent memory is mapped uncacheable on x86 and is shared, +unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes +directly to host-visible memory. A H/W can modify the value +between the check and the use, bypassing the bounds validation. + +Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a +stack-local variable in mana_hwc_rx_event_handler(), and passing the +validated value as a parameter to mana_hwc_handle_resp(). + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Erni Sri Satya Vennela +Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 48a9acea4ab6c..12d73470fd6bb 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -77,21 +77,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, + } + + static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, +- struct hwc_work_request *rx_req) ++ struct hwc_work_request *rx_req, u16 msg_id) + { + const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; + struct hwc_caller_ctx *ctx; + int err; + +- if (!test_bit(resp_msg->response.hwc_msg_id, +- hwc->inflight_msg_res.map)) { +- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", +- resp_msg->response.hwc_msg_id); ++ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { ++ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); + mana_hwc_post_rx_wqe(hwc->rxq, rx_req); + return; + } + +- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; ++ ctx = hwc->caller_ctx + msg_id; + err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); + if (err) + goto out; +@@ -251,6 +249,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + struct gdma_sge *sge; + u64 rq_base_addr; + u64 rx_req_idx; ++ u16 msg_id; + u8 *wqe; + + if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) +@@ -269,13 +268,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { +- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", +- resp->response.hwc_msg_id); ++ /* Read msg_id once from DMA buffer to prevent TOCTOU: ++ * DMA memory is shared/unencrypted in CVMs - host can ++ * modify it between reads. ++ */ ++ msg_id = READ_ONCE(resp->response.hwc_msg_id); ++ if (msg_id >= hwc->num_inflight_msg) { ++ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); + return; + } + +- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); ++ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); + + /* Can no longer use 'resp', because the buffer is posted to the HW + * in mana_hwc_handle_resp() above. +-- +2.53.0 + diff --git a/queue-7.0/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch b/queue-7.0/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch new file mode 100644 index 0000000000..fed7869293 --- /dev/null +++ b/queue-7.0/net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch @@ -0,0 +1,48 @@ +From 000b356ab8cfc2db9b28f2ba5effbf79896950a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 22:15:53 -0700 +Subject: net: mana: validate rx_req_idx to prevent out-of-bounds array access + +From: Aditya Garg + +[ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] + +In mana_hwc_rx_event_handler(), rx_req_idx is derived from +sge->address in DMA-coherent memory. In Confidential VMs +(SEV-SNP/TDX), this memory is shared unencrypted and HW can modify +WQE contents at any time. No bounds check exists on rx_req_idx, +which can lead to an out-of-bounds access into reqs[]. + +Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before +using it to index the reqs[] array. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Haiyang Zhang +Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c +index 12d73470fd6bb..dbaeedb6e7b1a 100644 +--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c ++++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c +@@ -265,6 +265,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, + rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; + rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + ++ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { ++ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", ++ rx_req_idx, hwc_rxq->msg_buf->num_reqs); ++ return; ++ } ++ + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; + resp = (struct gdma_resp_hdr *)rx_req->buf_va; + +-- +2.53.0 + diff --git a/queue-7.0/net-mlx5-do-not-restore-destination-less-tc-rules.patch b/queue-7.0/net-mlx5-do-not-restore-destination-less-tc-rules.patch new file mode 100644 index 0000000000..c70036a289 --- /dev/null +++ b/queue-7.0/net-mlx5-do-not-restore-destination-less-tc-rules.patch @@ -0,0 +1,55 @@ +From 54d4601186bdd724ea2094596247825897214167 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:33:02 +0300 +Subject: net/mlx5: Do not restore destination-less TC rules + +From: Jeroen Massar + +[ Upstream commit 8d0a5af8b1ba598e7340761729801624e7a9330e ] + +After IPsec policy/state TX rules are added, any TC flow rule, which +forwards packets to uplink, is modified to forward to IPsec TX tables. +As these tables are destroyed dynamically, whenever there is no +reference to them, the destinations of this kind of rules must be +restored to uplink, unless there is no destination for that rule. + +The flow rules FLOW_ACTION_ACCEPT, DROP, TRAP, GOTO and SAMPLE do not +have a destination port, and thus out_count = 0. + +At cleanup time of the rules in mlx5_esw_ipsec_modify_flow_dests +we call mlx5_eswitch_restore_ipsec_rule but as the above types +do not have a destination we get an underflow of out_count, as +the port is passed, which is esw_attr->out_count - 1. + +This change avoids calling mlx5_eswitch_restore_ipsec_rule when +there are no output destinations and thus avoids the underflow. + +Fixes: d1569537a837 ("net/mlx5e: Modify and restore TC rules for IPSec TX rules") +Signed-off-by: Jeroen Massar +Reviewed-by: Jianbo Liu +Reviewed-by: Cosmin Ratiu +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +index 3cfe743610d3f..ab50d2c734ede 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +@@ -142,7 +142,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, + + attr = flow->attr; + esw_attr = attr->esw_attr; +- if (esw_attr->out_count - esw_attr->split_count > 1) ++ if (!esw_attr->out_count || ++ esw_attr->out_count - esw_attr->split_count > 1) + return 0; + + err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, +-- +2.53.0 + diff --git a/queue-7.0/net-mlx5-skip-disabled-vports-when-setting-max-tx-sp.patch b/queue-7.0/net-mlx5-skip-disabled-vports-when-setting-max-tx-sp.patch new file mode 100644 index 0000000000..278d9c1020 --- /dev/null +++ b/queue-7.0/net-mlx5-skip-disabled-vports-when-setting-max-tx-sp.patch @@ -0,0 +1,100 @@ +From b114563126be3af31d9ed51d0a68c60ee7541d61 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:36:40 +0300 +Subject: net/mlx5: Skip disabled vports when setting max TX speed + +From: Or Har-Toov + +[ Upstream commit c6df9a65cbb0fe7808a4b2872095f4c849b3196a ] + +When setting vports max TX speed during LAG activation or bond state +changes, the code iterates over all eswitch vports. However, some +vports may not be enabled yet. + +Skip vports that are not enabled to avoid sending FW commands for +uninitialized vports. Save the LAG aggregated speed in the vport +struct so it can be applied when the vport is enabled later. + +Fixes: 50f1d188c580 ("net/mlx5: Propagate LAG effective max_tx_speed to vports") +Signed-off-by: Or Har-Toov +Reviewed-by: Mark Bloch +Signed-off-by: Tariq Toukan +Link: https://patch.msgid.link/20260513063640.334132-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../net/ethernet/mellanox/mlx5/core/eswitch.c | 21 +++++++++++++++++++ + .../net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + + .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 5 +++++ + 3 files changed, 27 insertions(+) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +index 123c96716a544..7c8311f412323 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +@@ -908,6 +908,24 @@ static void esw_vport_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport + esw_vport_cleanup_acl(esw, vport); + } + ++static void mlx5_esw_vport_set_max_tx_speed(struct mlx5_eswitch *esw, ++ struct mlx5_vport *vport) ++{ ++ int ret; ++ ++ if (!MLX5_CAP_ESW(esw->dev, esw_vport_state_max_tx_speed)) ++ return; ++ ++ ret = mlx5_modify_vport_max_tx_speed(esw->dev, ++ MLX5_VPORT_STATE_OP_MOD_ESW_VPORT, ++ vport->vport, true, ++ vport->agg_max_tx_speed); ++ if (ret) ++ mlx5_core_dbg(esw->dev, ++ "Failed to set vport %d speed %d, err=%d\n", ++ vport->vport, vport->agg_max_tx_speed, ret); ++} ++ + int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, + enum mlx5_eswitch_vport_event enabled_events) + { +@@ -948,6 +966,9 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, + + esw->enabled_vports++; + esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num); ++ ++ if (vport->agg_max_tx_speed) ++ mlx5_esw_vport_set_max_tx_speed(esw, vport); + done: + mutex_unlock(&esw->state_lock); + return ret; +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +index c2563bee74dfe..29cce1bbce941 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +@@ -247,6 +247,7 @@ struct mlx5_vport { + enum mlx5_eswitch_vport_event enabled_events; + int index; + struct mlx5_devlink_port *dl_port; ++ u32 agg_max_tx_speed; + }; + + struct mlx5_esw_indir_table; +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +index 044adfdf9aa26..82e884e70168c 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +@@ -1074,6 +1074,11 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev, + if (vport->vport == MLX5_VPORT_UPLINK) + continue; + ++ vport->agg_max_tx_speed = speed; ++ ++ if (!vport->enabled) ++ continue; ++ + ret = mlx5_modify_vport_max_tx_speed(mdev, op_mod, + vport->vport, true, speed); + if (ret) +-- +2.53.0 + diff --git a/queue-7.0/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch b/queue-7.0/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch new file mode 100644 index 0000000000..a78afe8889 --- /dev/null +++ b/queue-7.0/net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch @@ -0,0 +1,60 @@ +From b14641ae7cc5e0952e9e3f0b7acdca2ad9cd4f89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:59:00 +0100 +Subject: net/mlx5e: Fix eswitch mode block underflow on IPsec acquire SA + +From: Prathamesh Deshpande + +[ Upstream commit abe003b33223ff33552f291644bf35d9c2f992fb ] + +mlx5e_xfrm_add_state() handles acquire-flow temporary SAs by allocating +software state and skipping hardware offload setup. + +That path jumps to the common success label before taking the eswitch mode +block. After tunnel-mode validation was moved earlier, the common success +label unconditionally calls mlx5_eswitch_unblock_mode(). For acquire SAs, +this decrements esw->offloads.num_block_mode without a matching increment. + +Return directly after installing the acquire SA offload handle, so only the +paths that successfully called mlx5_eswitch_block_mode() call the matching +unblock. + +Fixes: 22239eb258bc ("net/mlx5e: Prevent tunnel reformat when tunnel mode not allowed") +Signed-off-by: Prathamesh Deshpande +Reviewed-by: Simon Horman +Reviewed-by: Tariq Toukan +Link: https://patch.msgid.link/20260510225903.13184-1-prathameshdeshpande7@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 7 ++++--- + 1 file changed, 4 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 64e13747084ee..9c1f3d734911f 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +@@ -793,8 +793,10 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, + sa_entry->dev = dev; + sa_entry->ipsec = ipsec; + /* Check if this SA is originated from acquire flow temporary SA */ +- if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) +- goto out; ++ if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) { ++ x->xso.offload_handle = (unsigned long)sa_entry; ++ return 0; ++ } + + err = mlx5e_xfrm_validate_state(priv->mdev, x, extack); + if (err) +@@ -871,7 +873,6 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, + xa_unlock_bh(&ipsec->sadb); + } + +-out: + x->xso.offload_handle = (unsigned long)sa_entry; + if (allow_tunnel_mode) + mlx5_eswitch_unblock_encap(priv->mdev); +-- +2.53.0 + diff --git a/queue-7.0/net-mlx5e-xsk-fix-unlocked-writing-to-icosq.patch b/queue-7.0/net-mlx5e-xsk-fix-unlocked-writing-to-icosq.patch new file mode 100644 index 0000000000..eae71dcec4 --- /dev/null +++ b/queue-7.0/net-mlx5e-xsk-fix-unlocked-writing-to-icosq.patch @@ -0,0 +1,106 @@ +From ce85096803736fdc420c9b503b41d368c7979101 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 09:46:13 +0300 +Subject: net/mlx5e: xsk: Fix unlocked writing to ICOSQ + +From: Dragos Tatulea + +[ Upstream commit c326f9c68921e2f14dfcecb2f6b4216313d50248 ] + +During napi poll, when the affinity changes and there's still XSK work +to be done, we trigger an ICOSQ interrupt on the new CPU. However, this +triggering on the ICOSQ is done unprotected. + +There are 2 such races: + +A) mlx5e_trigger_irq() is called while mlx5e_xsk_alloc_rx_mpwqe() is +running from a different CPU due to affinity change. This can happen +because IRQ triggering is done after napi_complete_done(). At this point +the NAPI can be scheduled on a different CPU. Like this: + + CPU A (old affinity, NAPI tail) CPU B (new affinity, fresh NAPI) + ------------------------------- -------------------------------- + napi_complete_done() clears SCHED + mlx5e_cq_arm(...) + napi_schedule_prep() sets SCHED + mlx5e_napi_poll() + mlx5e_xsk_alloc_rx_mpwqe() + mlx5e_icosq_sync_lock() // noop + memcpy 640 B UMR body + advance sq->pc by 10 + mlx5e_trigger_irq(&c->icosq) + wqe_info[pi] = {NOP, 1} + mlx5e_post_nop() advances sq->pc + +B) mlx5e_trigger_irq() is called on the ICOSQ when +mlx5e_trigger_napi_icosq() is running. + +The obvious fix would be to lock the ICOSQ. But ICOSQ has an optimized +locking scheme that doesn't work for this scenario. Kick the async ICOSQ +instead which is always locked. + +This issue was noticed in the wild with the following splat: + + netdevice: ge-0-0-1: Bad OP in ICOSQ CQE: 0xd + WARNING: drivers/net/ethernet/mellanox/mlx5/core/en_rx.c:826 [...] + [...] + Call Trace: + + mlx5e_napi_poll+0x11d/0x7f0 [mlx5_core] + __napi_poll+0x30/0x200 + ? skb_defer_free_flush+0x9c/0xc0 + net_rx_action+0x2fe/0x3f0 + handle_softirqs+0xd8/0x340 + __irq_exit_rcu+0xbc/0xe0 + common_interrupt+0x85/0xa0 + + + asm_common_interrupt+0x26/0x40 + [...] + ---[ end trace 0000000000000000 ]--- + mlx5_core 0000:08:00.0 ge-0-0-1: Error cqe on cqn 0x548, ci 0x2022, qn 0x8f4, + opcode 0xd, syndrome 0x2, vendor syndrome 0x68 + 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000030: 00 00 00 00 01 00 68 02 01 00 08 f4 de 14 59 d2 + WQE DUMP: WQ size 16384 WQ cur size 0, WQE index 0x1e14, len: 64 + 00000000: 00 00 00 01 d9 ed 80 02 00 00 00 01 d9 ed 90 02 + 00000010: 00 00 00 01 d9 ed a0 02 00 00 00 01 d9 ed b0 02 + 00000020: 00 00 00 01 d9 ed c0 02 00 00 00 01 d9 ed d0 02 + 00000030: 00 00 00 01 d9 ed e0 02 00 00 00 01 d9 ed f0 02 + mlx5_core 0000:08:00.0 ge-0-0-1: Error cqe on cqn 0x548, ci 0x2023, qn 0x8f4, + opcode 0xd, syndrome 0x5, vendor syndrome 0xf9 + 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00000030: 00 00 00 00 01 00 f9 05 01 00 08 f4 de 15 cf d2 + +Fixes: db05815b36cb ("net/mlx5e: Add XSK zero-copy support") +Reported-by: Paul Saab +Signed-off-by: Dragos Tatulea +Signed-off-by: Tariq Toukan +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260513064613.334602-1-tariqt@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +index b31f689fe271c..e90c6c6df835d 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +@@ -252,7 +252,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) + mlx5e_cq_arm(&c->xdpsq->cq); + + if (unlikely(aff_change && busy_xsk)) { +- mlx5e_trigger_irq(&c->icosq); ++ mlx5e_trigger_napi_async_icosq(c); + ch_stats->force_irq++; + } + +-- +2.53.0 + diff --git a/queue-7.0/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch b/queue-7.0/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch new file mode 100644 index 0000000000..dd9f96bda8 --- /dev/null +++ b/queue-7.0/net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch @@ -0,0 +1,95 @@ +From 38dce8586a574f70bc57936d44774c494e090d0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 09:08:08 +0000 +Subject: net: napi: Avoid gro timer misfiring at end of busypoll + +From: Dragos Tatulea + +[ Upstream commit 58e2330bd45572a6e3d46ea94cf7a9641f43591a ] + +When in irq deferral mode (defer-hard-irqs > 0), a short enough +gro-flush timeout can trigger before NAPI_STATE_SCHED is cleared if the +last poll in busy_poll_stop() takes too long. This can have the effect +of leaving the queue stuck with interrupts disabled and no timer armed +which results in a tx timeout if there is no subsequent busypoll cycle. + +To prevent this, defer the gro-flush timer arm after the last poll. + +Fixes: 7fd3253a7de6 ("net: Introduce preferred busy-polling") +Co-developed-by: Martin Karsten +Signed-off-by: Martin Karsten +Signed-off-by: Dragos Tatulea +Reviewed-by: Tariq Toukan +Reviewed-by: Cosmin Ratiu +Reviewed-by: Joe Damato +Link: https://patch.msgid.link/20260506090808.820559-2-dtatulea@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/core/dev.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/net/core/dev.c b/net/core/dev.c +index e4fcf09ba2beb..fab5a0bebd924 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6855,9 +6855,9 @@ static void skb_defer_free_flush(void) + + #if defined(CONFIG_NET_RX_BUSY_POLL) + +-static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) ++static void __busy_poll_stop(struct napi_struct *napi, unsigned long timeout) + { +- if (!skip_schedule) { ++ if (!timeout) { + gro_normal_list(&napi->gro); + __napi_schedule(napi); + return; +@@ -6867,6 +6867,8 @@ static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) + gro_flush_normal(&napi->gro, HZ >= 1000); + + clear_bit(NAPI_STATE_SCHED, &napi->state); ++ hrtimer_start(&napi->timer, ns_to_ktime(timeout), ++ HRTIMER_MODE_REL_PINNED); + } + + enum { +@@ -6878,8 +6880,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + unsigned flags, u16 budget) + { + struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx; +- bool skip_schedule = false; +- unsigned long timeout; ++ unsigned long timeout = 0; + int rc; + + /* Busy polling means there is a high chance device driver hard irq +@@ -6899,10 +6900,12 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + + if (flags & NAPI_F_PREFER_BUSY_POLL) { + napi->defer_hard_irqs_count = napi_get_defer_hard_irqs(napi); +- timeout = napi_get_gro_flush_timeout(napi); +- if (napi->defer_hard_irqs_count && timeout) { +- hrtimer_start(&napi->timer, ns_to_ktime(timeout), HRTIMER_MODE_REL_PINNED); +- skip_schedule = true; ++ if (napi->defer_hard_irqs_count) { ++ /* A short enough gro flush timeout and long enough ++ * poll can result in timer firing too early. ++ * Timer will be armed later if necessary. ++ */ ++ timeout = napi_get_gro_flush_timeout(napi); + } + } + +@@ -6917,7 +6920,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, + trace_napi_poll(napi, rc, budget); + netpoll_poll_unlock(have_poll_lock); + if (rc == budget) +- __busy_poll_stop(napi, skip_schedule); ++ __busy_poll_stop(napi, timeout); + bpf_net_ctx_clear(bpf_net_ctx); + local_bh_enable(); + } +-- +2.53.0 + diff --git a/queue-7.0/net-phy-dp83tc811-add-reading-of-abilities.patch b/queue-7.0/net-phy-dp83tc811-add-reading-of-abilities.patch new file mode 100644 index 0000000000..1019d163e7 --- /dev/null +++ b/queue-7.0/net-phy-dp83tc811-add-reading-of-abilities.patch @@ -0,0 +1,40 @@ +From 4f73884d5be44eeed529941f1c9dfd863f8f81f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 09:19:47 +0200 +Subject: net: phy: DP83TC811: add reading of abilities + +From: Sven Schuchmann + +[ Upstream commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac ] + +At this time the driver is not listing any speeds +it supports. This should be ETHTOOL_LINK_MODE_100baseT1_Full_BIT +for DP83TC811. Add the missing call for phylib to read the abilities. + +Fixes: b753a9faaf9a ("net: phy: DP83TC811: Introduce support for the DP83TC811 phy") +Suggested-by: Andrew Lunn +Signed-off-by: Sven Schuchmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de +[pabeni@redhat.com: dropped revision history] +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/phy/dp83tc811.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c +index e480c2a074505..252fb12b3e68e 100644 +--- a/drivers/net/phy/dp83tc811.c ++++ b/drivers/net/phy/dp83tc811.c +@@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { + .config_init = dp83811_config_init, + .config_aneg = dp83811_config_aneg, + .soft_reset = dp83811_phy_reset, ++ .get_features = genphy_c45_pma_read_ext_abilities, + .get_wol = dp83811_get_wol, + .set_wol = dp83811_set_wol, + .config_intr = dp83811_config_intr, +-- +2.53.0 + diff --git a/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch b/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch new file mode 100644 index 0000000000..384195401b --- /dev/null +++ b/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch @@ -0,0 +1,46 @@ +From a0c74398198f247261ad78ead79548856e7a5226 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:23:10 +0200 +Subject: net: phy: honor eee_disabled_modes in phy_advertise_eee_all() + +From: Nicolai Buchwitz + +[ Upstream commit 8baa7506d793f0636e3f6f01b01ef7be19674d06 ] + +phy_advertise_eee_all() copies supported_eee into advertising_eee +unconditionally, overwriting any filtering applied during phy_probe() +based on DT eee-broken-* properties or driver-populated +eee_disabled_modes. genphy_c45_ethtool_set_eee() calls this helper +when user space passes an empty advertisement, undoing the filtering. + +Apply the same eee_disabled_modes mask in phy_advertise_eee_all() so +the filtering survives the copy, matching the pattern in phy_probe() +and phy_support_eee(). + +Fixes: b64691274f5d ("net: phy: add helper phy_advertise_eee_all") +Signed-off-by: Nicolai Buchwitz +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-2-05b52626fa68@tipi-net.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phy_device.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 893ad97fc60c3..cfb505ed9a3a0 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2907,7 +2907,8 @@ EXPORT_SYMBOL(phy_advertise_supported); + */ + void phy_advertise_eee_all(struct phy_device *phydev) + { +- linkmode_copy(phydev->advertising_eee, phydev->supported_eee); ++ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, ++ phydev->eee_disabled_modes); + } + EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + +-- +2.53.0 + diff --git a/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch b/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch new file mode 100644 index 0000000000..9dd735d64d --- /dev/null +++ b/queue-7.0/net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch @@ -0,0 +1,51 @@ +From 903d5b5174dcc9f810f987f1f1c0e34eb12cc15b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:23:09 +0200 +Subject: net: phy: honor eee_disabled_modes in phy_support_eee() + +From: Nicolai Buchwitz + +[ Upstream commit 3655063e083889ed4b79b7dda9cec65478dce09a ] + +phy_support_eee() copies supported_eee into advertising_eee +unconditionally, overwriting any filtering applied during phy_probe() +based on DT eee-broken-* properties or driver-populated +eee_disabled_modes. MAC drivers that call phy_support_eee() after +probe (e.g. bcmgenet, fec, lan743x, lan78xx, r8169) then cause the PHY +to advertise EEE for modes the user marked as broken. + +The symptom is that ethtool --show-eee on the local interface reports +"not supported" (supported & ~eee_disabled_modes is empty) while the +link partner sees EEE negotiated and active. + +phy_probe() already filters advertising_eee via eee_disabled_modes +after calling of_set_phy_eee_broken(). Apply the same mask in +phy_support_eee() so the filtering survives the copy. + +Fixes: 49168d1980e2 ("net: phy: Add phy_support_eee() indicating MAC support EEE") +Signed-off-by: Nicolai Buchwitz +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-1-05b52626fa68@tipi-net.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phy_device.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index f3696d9819d35..893ad97fc60c3 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2933,7 +2933,8 @@ EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + */ + void phy_support_eee(struct phy_device *phydev) + { +- linkmode_copy(phydev->advertising_eee, phydev->supported_eee); ++ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, ++ phydev->eee_disabled_modes); + phydev->eee_cfg.tx_lpi_enabled = true; + phydev->eee_cfg.eee_enabled = true; + } +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-annotate-the-data-races.patch b/queue-7.0/net-shaper-annotate-the-data-races.patch new file mode 100644 index 0000000000..0b560fc224 --- /dev/null +++ b/queue-7.0/net-shaper-annotate-the-data-races.patch @@ -0,0 +1,116 @@ +From d36c15a7d9b8531cd8f3a7fc5f27b9293fc48c2b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:13:24 -0700 +Subject: net: shaper: annotate the data races + +From: Jakub Kicinski + +[ Upstream commit a3442936dd0523277e20aaf86207c574e755c634 ] + +As previously discussed we don't care about making the shaper +state fully RCU-compliant because the hierarchy itself can't +be dumped in one go over Netlink. Let's annotate the reads +and writes to make that clear. + +The field-by-field assignments will also be useful for the +next commit which adds explicit "valid" field (which we don't +want to override with the current full struct assignment). + +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260515221325.1685455-2-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: b8d7519352ba ("net: shaper: rework the VALID marking (again)") +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 53 ++++++++++++++++++++++++++++++++------------- + 1 file changed, 38 insertions(+), 15 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index b1c65110f04d3..520cefdc3d908 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -138,35 +138,58 @@ static int net_shaper_fill_handle(struct sk_buff *msg, + return -EMSGSIZE; + } + ++static void net_shaper_copy(struct net_shaper *dst, ++ const struct net_shaper *src) ++{ ++ WRITE_ONCE(dst->parent.scope, READ_ONCE(src->parent.scope)); ++ WRITE_ONCE(dst->parent.id, READ_ONCE(src->parent.id)); ++ WRITE_ONCE(dst->handle.scope, READ_ONCE(src->handle.scope)); ++ WRITE_ONCE(dst->handle.id, READ_ONCE(src->handle.id)); ++ ++ WRITE_ONCE(dst->metric, READ_ONCE(src->metric)); ++ WRITE_ONCE(dst->bw_min, READ_ONCE(src->bw_min)); ++ WRITE_ONCE(dst->bw_max, READ_ONCE(src->bw_max)); ++ WRITE_ONCE(dst->burst, READ_ONCE(src->burst)); ++ WRITE_ONCE(dst->priority, READ_ONCE(src->priority)); ++ WRITE_ONCE(dst->weight, READ_ONCE(src->weight)); ++ ++ /* private fields are only used on the write path under the lock */ ++ data_race(dst->leaves = src->leaves); ++} ++ + static int + net_shaper_fill_one(struct sk_buff *msg, + const struct net_shaper_binding *binding, + const struct net_shaper *shaper, + const struct genl_info *info) + { ++ struct net_shaper cur; + void *hdr; + + hdr = genlmsg_iput(msg, info); + if (!hdr) + return -EMSGSIZE; + ++ /* Make a copy to avoid data races */ ++ net_shaper_copy(&cur, shaper); ++ + if (net_shaper_fill_binding(msg, binding, NET_SHAPER_A_IFINDEX) || +- net_shaper_fill_handle(msg, &shaper->parent, ++ net_shaper_fill_handle(msg, &cur.parent, + NET_SHAPER_A_PARENT) || +- net_shaper_fill_handle(msg, &shaper->handle, ++ net_shaper_fill_handle(msg, &cur.handle, + NET_SHAPER_A_HANDLE) || +- ((shaper->bw_min || shaper->bw_max || shaper->burst) && +- nla_put_u32(msg, NET_SHAPER_A_METRIC, shaper->metric)) || +- (shaper->bw_min && +- nla_put_uint(msg, NET_SHAPER_A_BW_MIN, shaper->bw_min)) || +- (shaper->bw_max && +- nla_put_uint(msg, NET_SHAPER_A_BW_MAX, shaper->bw_max)) || +- (shaper->burst && +- nla_put_uint(msg, NET_SHAPER_A_BURST, shaper->burst)) || +- (shaper->priority && +- nla_put_u32(msg, NET_SHAPER_A_PRIORITY, shaper->priority)) || +- (shaper->weight && +- nla_put_u32(msg, NET_SHAPER_A_WEIGHT, shaper->weight))) ++ ((cur.bw_min || cur.bw_max || cur.burst) && ++ nla_put_u32(msg, NET_SHAPER_A_METRIC, cur.metric)) || ++ (cur.bw_min && ++ nla_put_uint(msg, NET_SHAPER_A_BW_MIN, cur.bw_min)) || ++ (cur.bw_max && ++ nla_put_uint(msg, NET_SHAPER_A_BW_MAX, cur.bw_max)) || ++ (cur.burst && ++ nla_put_uint(msg, NET_SHAPER_A_BURST, cur.burst)) || ++ (cur.priority && ++ nla_put_u32(msg, NET_SHAPER_A_PRIORITY, cur.priority)) || ++ (cur.weight && ++ nla_put_u32(msg, NET_SHAPER_A_WEIGHT, cur.weight))) + goto nla_put_failure; + + genlmsg_end(msg, hdr); +@@ -424,7 +447,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- *cur = shapers[i]; ++ net_shaper_copy(cur, &shapers[i]); + smp_wmb(); + __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + } +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch b/queue-7.0/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch new file mode 100644 index 0000000000..c3d20670aa --- /dev/null +++ b/queue-7.0/net-shaper-enforce-singleton-netdev-scope-with-id-0.patch @@ -0,0 +1,46 @@ +From 4c83863bafc189f07c692004f8ff59ba48f50395 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:03 -0700 +Subject: net: shaper: enforce singleton NETDEV scope with id 0 + +From: Jakub Kicinski + +[ Upstream commit b62b29e6de6711f5918940aa6ff2bbab6d6af502 ] + +The NETDEV scope represents a singleton root shaper in the per-device +hierarchy. All code assumes NETDEV shapers have id 0: +net_shaper_default_parent() hardcodes parent->id = 0 when returning +the NETDEV parent for QUEUE/NODE children, and the UAPI documentation +describes NETDEV scope as "the main shaper" (singular, not plural). + +Make sure we reject non-0 IDs. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index eb049847fed65..4ae3ee6764a0a 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -482,6 +482,12 @@ static int net_shaper_parse_handle(const struct nlattr *attr, + else if (handle->scope == NET_SHAPER_SCOPE_NODE) + id = NET_SHAPER_ID_UNSPEC; + ++ if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { ++ NL_SET_ERR_MSG_ATTR(info->extack, id_attr, ++ "Netdev scope is a singleton, must use ID 0"); ++ return -EINVAL; ++ } ++ + handle->id = id; + return 0; + } +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch b/queue-7.0/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch new file mode 100644 index 0000000000..91aca02825 --- /dev/null +++ b/queue-7.0/net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch @@ -0,0 +1,60 @@ +From e14a4d58f61a67c9cd664bb74a31568a5ef009d9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:56 -0700 +Subject: net: shaper: fix trivial ordering issue in net_shaper_commit() + +From: Jakub Kicinski + +[ Upstream commit 235fb5376139c3419f2218349f1fa2f06f24f7ad ] + +We should update the entry before we mark it as valid. + +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index d2b8f1f951b19..86319ddbf2905 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -295,6 +295,10 @@ net_shaper_lookup(struct net_shaper_binding *binding, + NET_SHAPER_VALID)) + return NULL; + ++ /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is ++ * valid, its contents must be visible too. ++ */ ++ smp_rmb(); + return xa_load(&hierarchy->shapers, index); + } + +@@ -412,8 +416,9 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + *cur = shapers[i]; ++ smp_wmb(); ++ __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + } + xa_unlock(&hierarchy->shapers); + } +@@ -837,6 +842,10 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, + U32_MAX, NET_SHAPER_VALID)); + ctx->start_index++) { ++ /* Pairs with smp_wmb() in net_shaper_commit(): the entry ++ * is marked VALID, so its contents must be visible too. ++ */ ++ smp_rmb(); + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch b/queue-7.0/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch new file mode 100644 index 0000000000..1634b31074 --- /dev/null +++ b/queue-7.0/net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch @@ -0,0 +1,72 @@ +From 17cce851a28b5595a93ffd109d3b76e8ecd285bf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:00 -0700 +Subject: net: shaper: fix undersized reply skb allocation in GROUP command + +From: Jakub Kicinski + +[ Upstream commit 0f9a857e34d0f8c018a3e4435c6f0e92e8d2f38c ] + +net_shaper_group_send_reply() writes both the NET_SHAPER_A_IFINDEX +attribute (via net_shaper_fill_binding()) and the nested +NET_SHAPER_A_HANDLE attribute (via net_shaper_fill_handle()), but +the reply skb at the call site in net_shaper_nl_group_doit() is +allocated using net_shaper_handle_size(), which only accounts for +the nested handle. + +The allocation is therefore short by nla_total_size(sizeof(u32)) +(8 bytes) for the IFINDEX attribute. In practice the slab allocator +rounds up the small allocation so the bug is latent, but the size +accounting is wrong and could bite if the reply grew further. + +Introduce net_shaper_group_reply_size() that accounts for the full +reply payload and use it both at the genlmsg_new() call site and in +the defensive WARN_ONCE message. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 12e5e0c18643b..08fde2d9e8aa8 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -90,6 +90,12 @@ static int net_shaper_handle_size(void) + nla_total_size(sizeof(u32))); + } + ++static int net_shaper_group_reply_size(void) ++{ ++ return nla_total_size(sizeof(u32)) + /* NET_SHAPER_A_IFINDEX */ ++ net_shaper_handle_size(); /* NET_SHAPER_A_HANDLE */ ++} ++ + static int net_shaper_fill_binding(struct sk_buff *msg, + const struct net_shaper_binding *binding, + u32 type) +@@ -1227,7 +1233,7 @@ static int net_shaper_group_send_reply(struct net_shaper_binding *binding, + free_msg: + /* Should never happen as msg is pre-allocated with enough space. */ + WARN_ONCE(true, "calculated message payload length (%d)", +- net_shaper_handle_size()); ++ net_shaper_group_reply_size()); + nlmsg_free(msg); + return -EMSGSIZE; + } +@@ -1275,7 +1281,7 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + /* Prepare the msg reply in advance, to avoid device operation + * rollback on allocation failure. + */ +- msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); ++ msg = genlmsg_new(net_shaper_group_reply_size(), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto free_leaves; +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-flip-the-polarity-of-the-valid-flag.patch b/queue-7.0/net-shaper-flip-the-polarity-of-the-valid-flag.patch new file mode 100644 index 0000000000..995474c5ae --- /dev/null +++ b/queue-7.0/net-shaper-flip-the-polarity-of-the-valid-flag.patch @@ -0,0 +1,112 @@ +From dd26f534ccb63f63925fd0dc51eac2860602f16c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:55 -0700 +Subject: net: shaper: flip the polarity of the valid flag + +From: Jakub Kicinski + +[ Upstream commit 7cee43fcb0c3f71441d2faaa8c2202b6a88b6bef ] + +The usual way of inserting entries which are not yet fully ready +into XArray is to have a VALID flag. The shaper code has a NOT_VALID +flag. Since XArray code does not let us create entries with marks +already set - the creation of entries is currently not atomic. + +Flip the polarity of the VALID flag. This closes the tiny race +in net_shaper_pre_insert() of entries being created without +the NOT_VALID flag. + +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 34 +++++++++++++++++----------------- + 1 file changed, 17 insertions(+), 17 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 1069fa4eb9f60..d2b8f1f951b19 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -275,11 +275,13 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, + parent->id = 0; + } + +-/* +- * MARK_0 is already in use due to XA_FLAGS_ALLOC, can't reuse such flag as +- * it's cleared by xa_store(). ++/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on ++ * an entry only after the device-side configuration has completed ++ * successfully (see net_shaper_commit()). Lookups and dumps must filter on ++ * this mark to avoid exposing tentative entries inserted by ++ * net_shaper_pre_insert() while the driver call is still in flight. + */ +-#define NET_SHAPER_NOT_VALID XA_MARK_1 ++#define NET_SHAPER_VALID XA_MARK_1 + + static struct net_shaper * + net_shaper_lookup(struct net_shaper_binding *binding, +@@ -289,8 +291,8 @@ net_shaper_lookup(struct net_shaper_binding *binding, + struct net_shaper_hierarchy *hierarchy; + + hierarchy = net_shaper_hierarchy_rcu(binding); +- if (!hierarchy || xa_get_mark(&hierarchy->shapers, index, +- NET_SHAPER_NOT_VALID)) ++ if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, ++ NET_SHAPER_VALID)) + return NULL; + + return xa_load(&hierarchy->shapers, index); +@@ -370,13 +372,10 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, + goto free_id; + } + +- /* Mark 'tentative' shaper inside the hierarchy container. +- * xa_set_mark is a no-op if the previous store fails. ++ /* Insert as 'tentative' (no VALID mark). The mark will be set by ++ * net_shaper_commit() once the driver-side configuration succeeds. + */ +- xa_lock(&hierarchy->shapers); +- prev = __xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_NOT_VALID); +- xa_unlock(&hierarchy->shapers); ++ prev = xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); + if (xa_err(prev)) { + NL_SET_ERR_MSG(extack, "Can't insert shaper into device store"); + kfree_rcu(cur, rcu); +@@ -413,8 +412,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + /* Successful update: drop the tentative mark + * and update the hierarchy container. + */ +- __xa_clear_mark(&hierarchy->shapers, index, +- NET_SHAPER_NOT_VALID); ++ __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + *cur = shapers[i]; + } + xa_unlock(&hierarchy->shapers); +@@ -431,8 +429,9 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) + return; + + xa_lock(&hierarchy->shapers); +- xa_for_each_marked(&hierarchy->shapers, index, cur, +- NET_SHAPER_NOT_VALID) { ++ xa_for_each(&hierarchy->shapers, index, cur) { ++ if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) ++ continue; + __xa_erase(&hierarchy->shapers, index); + kfree(cur); + } +@@ -836,7 +835,8 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + goto out_unlock; + + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, +- U32_MAX, XA_PRESENT)); ctx->start_index++) { ++ U32_MAX, NET_SHAPER_VALID)); ++ ctx->start_index++) { + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-reject-duplicate-leaves-in-group-request.patch b/queue-7.0/net-shaper-reject-duplicate-leaves-in-group-request.patch new file mode 100644 index 0000000000..93a94961d1 --- /dev/null +++ b/queue-7.0/net-shaper-reject-duplicate-leaves-in-group-request.patch @@ -0,0 +1,117 @@ +From f96f73a54bee4aefe5e2660b3febdd947c33acfc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:57 -0700 +Subject: net: shaper: reject duplicate leaves in GROUP request + +From: Jakub Kicinski + +[ Upstream commit a9a2fa1da619f276580b0d4c5d12efac89e8642b ] + +net_shaper_nl_group_doit() does not deduplicate NET_SHAPER_A_LEAVES +entries. When userspace supplies the same leaf handle twice, the same +old-parent pointer lands twice in old_nodes[]. The cleanup loop double +frees the parent. Of course the same parent may still be in old_nodes[] +twice if we are moving multiple of its leaves. + +Note that this patch also implicitly fixes the fact that the +i >= leaves_count path forgets to set ret. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-4-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 60 +++++++++++++++++++++++++++++++++------------ + 1 file changed, 45 insertions(+), 15 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 86319ddbf2905..c8960821cf236 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -941,6 +941,46 @@ static int net_shaper_handle_cmp(const struct net_shaper_handle *a, + return memcmp(a, b, sizeof(*a)); + } + ++static int net_shaper_parse_leaves(struct net_shaper_binding *binding, ++ struct genl_info *info, ++ const struct net_shaper *node, ++ struct net_shaper *leaves, ++ int leaves_count) ++{ ++ struct nlattr *attr; ++ int i, j, ret, rem; ++ ++ i = 0; ++ nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, ++ genlmsg_data(info->genlhdr), ++ genlmsg_len(info->genlhdr), rem) { ++ if (WARN_ON_ONCE(i >= leaves_count)) ++ return -EINVAL; ++ ++ ret = net_shaper_parse_leaf(binding, attr, info, ++ node, &leaves[i]); ++ if (ret) ++ return ret; ++ ++ /* Reject duplicates */ ++ for (j = 0; j < i; j++) { ++ if (net_shaper_handle_cmp(&leaves[i].handle, ++ &leaves[j].handle)) ++ continue; ++ ++ NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, ++ "Duplicate leaf shaper %d:%d", ++ leaves[i].handle.scope, ++ leaves[i].handle.id); ++ return -EINVAL; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ + static int net_shaper_parent_from_leaves(int leaves_count, + const struct net_shaper *leaves, + struct net_shaper *node, +@@ -1197,10 +1237,9 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + struct net_shaper **old_nodes, *leaves, node = {}; + struct net_shaper_hierarchy *hierarchy; + struct net_shaper_binding *binding; +- int i, ret, rem, leaves_count; ++ int i, ret, leaves_count; + int old_nodes_count = 0; + struct sk_buff *msg; +- struct nlattr *attr; + + if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_LEAVES)) + return -EINVAL; +@@ -1228,19 +1267,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + if (ret) + goto free_leaves; + +- i = 0; +- nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, +- genlmsg_data(info->genlhdr), +- genlmsg_len(info->genlhdr), rem) { +- if (WARN_ON_ONCE(i >= leaves_count)) +- goto free_leaves; +- +- ret = net_shaper_parse_leaf(binding, attr, info, +- &node, &leaves[i]); +- if (ret) +- goto free_leaves; +- i++; +- } ++ ret = net_shaper_parse_leaves(binding, info, &node, ++ leaves, leaves_count); ++ if (ret) ++ goto free_leaves; + + /* Prepare the msg reply in advance, to avoid device operation + * rollback on allocation failure. +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch b/queue-7.0/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch new file mode 100644 index 0000000000..38e9bf2cf7 --- /dev/null +++ b/queue-7.0/net-shaper-reject-handle-ids-exceeding-internal-bit-.patch @@ -0,0 +1,121 @@ +From b633a86e382ed381c1efad0c8723f68ffe990a2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:02 -0700 +Subject: net: shaper: reject handle IDs exceeding internal bit-width + +From: Jakub Kicinski + +[ Upstream commit 8d5806c600fddb907ebe378f9c366d4b52ac3a39 ] + +net_shaper_parse_handle() reads the user-supplied handle ID via +nla_get_u32(), accepting the full u32 range. However, the xarray key +is built by net_shaper_handle_to_index() using +FIELD_PREP(NET_SHAPER_ID_MASK, handle->id), where NET_SHAPER_ID_MASK +is GENMASK(25, 0) - only 26 bits wide. FIELD_PREP silently masks off +the upper bits at runtime. A user-supplied NODE id like 0x04000123 +becomes id 0x123. + +Additionally, a user-supplied id equal to NET_SHAPER_ID_UNSPEC +(0x03FFFFFF, which is NET_SHAPER_ID_MASK itself) would collide with +the sentinel used internally by the group operation to signal +"allocate a new NODE id". + +Reject user-supplied IDs >= NET_SHAPER_ID_MASK (i.e., >= 0x03FFFFFF) +in the policy. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-9-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + Documentation/netlink/specs/net_shaper.yaml | 7 +++++++ + net/shaper/shaper.c | 4 +++- + net/shaper/shaper_nl_gen.c | 7 ++++++- + net/shaper/shaper_nl_gen.h | 2 ++ + 4 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml +index 3f2ad772b64b1..de01f922040a5 100644 +--- a/Documentation/netlink/specs/net_shaper.yaml ++++ b/Documentation/netlink/specs/net_shaper.yaml +@@ -33,6 +33,11 @@ doc: | + @cap-get operation. + + definitions: ++ - ++ type: const ++ name: max-handle-id ++ value: 0x3fffffe ++ scope: kernel + - + type: enum + name: scope +@@ -140,6 +145,8 @@ attribute-sets: + - + name: id + type: u32 ++ checks: ++ max: max-handle-id + doc: | + Numeric identifier of a shaper. The id semantic depends on + the scope. For @queue scope it's the queue id and for @node +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 08fde2d9e8aa8..eb049847fed65 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -21,6 +21,8 @@ + + #define NET_SHAPER_ID_UNSPEC NET_SHAPER_ID_MASK + ++static_assert(NET_SHAPER_ID_UNSPEC == NET_SHAPER_MAX_HANDLE_ID + 1); ++ + struct net_shaper_hierarchy { + struct xarray shapers; + }; +@@ -360,7 +362,7 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, + handle->id == NET_SHAPER_ID_UNSPEC) { + u32 min, max; + +- handle->id = NET_SHAPER_ID_MASK - 1; ++ handle->id = NET_SHAPER_MAX_HANDLE_ID; + max = net_shaper_handle_to_index(handle); + handle->id = 0; + min = net_shaper_handle_to_index(handle); +diff --git a/net/shaper/shaper_nl_gen.c b/net/shaper/shaper_nl_gen.c +index 9b29be3ef19a8..76eff85ec66df 100644 +--- a/net/shaper/shaper_nl_gen.c ++++ b/net/shaper/shaper_nl_gen.c +@@ -11,10 +11,15 @@ + + #include + ++/* Integer value ranges */ ++static const struct netlink_range_validation net_shaper_a_handle_id_range = { ++ .max = NET_SHAPER_MAX_HANDLE_ID, ++}; ++ + /* Common nested types */ + const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1] = { + [NET_SHAPER_A_HANDLE_SCOPE] = NLA_POLICY_MAX(NLA_U32, 3), +- [NET_SHAPER_A_HANDLE_ID] = { .type = NLA_U32, }, ++ [NET_SHAPER_A_HANDLE_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &net_shaper_a_handle_id_range), + }; + + const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1] = { +diff --git a/net/shaper/shaper_nl_gen.h b/net/shaper/shaper_nl_gen.h +index 42c46c52c7751..2406652a9014a 100644 +--- a/net/shaper/shaper_nl_gen.h ++++ b/net/shaper/shaper_nl_gen.h +@@ -12,6 +12,8 @@ + + #include + ++#define NET_SHAPER_MAX_HANDLE_ID 67108862 ++ + /* Common nested types */ + extern const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1]; + extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1]; +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-reject-queue-scope-handle-with-missing-id.patch b/queue-7.0/net-shaper-reject-queue-scope-handle-with-missing-id.patch new file mode 100644 index 0000000000..ea00919b2f --- /dev/null +++ b/queue-7.0/net-shaper-reject-queue-scope-handle-with-missing-id.patch @@ -0,0 +1,55 @@ +From bf339d36e5bb44703b3c28f002c6ed96bac1be4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:29:04 -0700 +Subject: net: shaper: reject QUEUE scope handle with missing id + +From: Jakub Kicinski + +[ Upstream commit ce372e869f9f492f3d5aa9a0ae75ed52c61d2d6f ] + +net_shaper_parse_handle() does not enforce that the user provides +the handle ID. For NODE the ID defaults to UNSPEC for all other +cases it defaults to 0. + +For NETDEV 0 is the only option. For QUEUE defaulting to 0 makes +less intuitive sense. Specifically because the behavior should +(IMHO) be the same for all cases where there may be more than +one ID (QUEUE and NODE). + +We should either document this as intentional or reject. +I picked the latter with no strong conviction. + +Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 4ae3ee6764a0a..b1c65110f04d3 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -477,10 +477,15 @@ static int net_shaper_parse_handle(const struct nlattr *attr, + * shaper (any other value). + */ + id_attr = tb[NET_SHAPER_A_HANDLE_ID]; +- if (id_attr) ++ if (id_attr) { + id = nla_get_u32(id_attr); +- else if (handle->scope == NET_SHAPER_SCOPE_NODE) ++ } else if (handle->scope == NET_SHAPER_SCOPE_NODE) { + id = NET_SHAPER_ID_UNSPEC; ++ } else if (handle->scope == NET_SHAPER_SCOPE_QUEUE) { ++ NL_SET_ERR_ATTR_MISS(info->extack, attr, ++ NET_SHAPER_A_HANDLE_ID); ++ return -EINVAL; ++ } + + if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { + NL_SET_ERR_MSG_ATTR(info->extack, id_attr, +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-reject-reparenting-of-existing-nodes.patch b/queue-7.0/net-shaper-reject-reparenting-of-existing-nodes.patch new file mode 100644 index 0000000000..f3a5eae7ef --- /dev/null +++ b/queue-7.0/net-shaper-reject-reparenting-of-existing-nodes.patch @@ -0,0 +1,90 @@ +From e62cedcbebb009a7f6acbbd979399b1133ee389b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 16:37:45 -0700 +Subject: net: shaper: Reject reparenting of existing nodes + +From: Mohsin Bashir + +[ Upstream commit a77d5a069d959dc45f5f472d48cba37d8cba0f1c ] + +When an existing node-scope shaper is moved to a different parent +via the group operation, the framework fails to update the leaves +count on both the old and new parent shapers. Only newly created +nodes (handle.id == NET_SHAPER_ID_UNSPEC) trigger the parent +leaves increment at line 1039. + +This causes the parent's leaves counter to diverge from the +actual number of children in the xarray. When the node is later +deleted, pre_del_node() allocates an array sized by the stale +leaves count, but the xarray iteration finds more children than +expected, hitting the WARN_ON_ONCE guard and returning -EINVAL. + +Rather than adding reparenting support with complex leaves count +bookkeeping, reject group calls that attempt to change an existing +node's parent. Updates to an existing node's rate or leaves under +the same parent remain permitted. We expect that for any modification +of the topology user should always create new groups and let the +kernel garbage collect the leaf-less nodes. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Mohsin Bashir +Link: https://patch.msgid.link/20260506233745.111895-1-mohsin.bashr@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 30 +++++++++++++++++++++++------- + 1 file changed, 23 insertions(+), 7 deletions(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 94bc9c7382ea6..1069fa4eb9f60 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -964,15 +964,22 @@ static int __net_shaper_group(struct net_shaper_binding *binding, + int i, ret; + + if (node->handle.scope == NET_SHAPER_SCOPE_NODE) { ++ struct net_shaper *cur = NULL; ++ + new_node = node->handle.id == NET_SHAPER_ID_UNSPEC; + +- if (!new_node && !net_shaper_lookup(binding, &node->handle)) { +- /* The related attribute is not available when +- * reaching here from the delete() op. +- */ +- NL_SET_ERR_MSG_FMT(extack, "Node shaper %d:%d does not exists", +- node->handle.scope, node->handle.id); +- return -ENOENT; ++ if (!new_node) { ++ cur = net_shaper_lookup(binding, &node->handle); ++ if (!cur) { ++ /* The related attribute is not available ++ * when reaching here from the delete() op. ++ */ ++ NL_SET_ERR_MSG_FMT(extack, ++ "Node shaper %d:%d does not exist", ++ node->handle.scope, ++ node->handle.id); ++ return -ENOENT; ++ } + } + + /* When unspecified, the node parent scope is inherited from +@@ -986,6 +993,15 @@ static int __net_shaper_group(struct net_shaper_binding *binding, + return ret; + } + ++ if (cur && net_shaper_handle_cmp(&cur->parent, ++ &node->parent)) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "Cannot reparent node shaper %d:%d", ++ node->handle.scope, ++ node->handle.id); ++ return -EOPNOTSUPP; ++ } ++ + } else { + net_shaper_default_parent(&node->handle, &node->parent); + } +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-rework-the-valid-marking-again.patch b/queue-7.0/net-shaper-rework-the-valid-marking-again.patch new file mode 100644 index 0000000000..642c33250b --- /dev/null +++ b/queue-7.0/net-shaper-rework-the-valid-marking-again.patch @@ -0,0 +1,143 @@ +From d9e41acc701c2d474296d734a2f8576edfd06721 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:13:25 -0700 +Subject: net: shaper: rework the VALID marking (again) + +From: Jakub Kicinski + +[ Upstream commit b8d7519352ba8c6df83259295d4a3bad093cae90 ] + +Recent commit changed the semantics from NOT_VALID to VALID. +I didn't realize that the flags are not stored atomically +with the entry in XArray. There's still a race of reader +observing a VALID mark for a slot, getting interrupted, +writer replacing the entry with a different one, reader +continuing, fetching the entry which is now a different +pointer than the pointer for which VALID was meant. + +The biggest consequence of this is that we may see a UAF +since net_shaper_rollback() assumed that entries without +VALID can be freed without observing RCU. + +Looks like the XArray marks are buying us nothing at this +point. Let's convert the code to an explicit valid field. +The smp_load_acquire() / smp_store_release() barriers are +marginally cleaner. + +Reported-by: Sashiko +Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260515221325.1685455-3-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/net_shaper.h | 1 + + net/shaper/shaper.c | 45 ++++++++++++++++------------------------ + 2 files changed, 19 insertions(+), 27 deletions(-) + +diff --git a/include/net/net_shaper.h b/include/net/net_shaper.h +index 5c3f49b52fe96..3939b816b0011 100644 +--- a/include/net/net_shaper.h ++++ b/include/net/net_shaper.h +@@ -53,6 +53,7 @@ struct net_shaper { + + /* private: */ + u32 leaves; /* accounted only for NODE scope */ ++ bool valid; + struct rcu_head rcu; + }; + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index 520cefdc3d908..dea9270f3e57d 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -306,31 +306,24 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, + parent->id = 0; + } + +-/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on +- * an entry only after the device-side configuration has completed +- * successfully (see net_shaper_commit()). Lookups and dumps must filter on +- * this mark to avoid exposing tentative entries inserted by +- * net_shaper_pre_insert() while the driver call is still in flight. +- */ +-#define NET_SHAPER_VALID XA_MARK_1 +- + static struct net_shaper * + net_shaper_lookup(struct net_shaper_binding *binding, + const struct net_shaper_handle *handle) + { + u32 index = net_shaper_handle_to_index(handle); + struct net_shaper_hierarchy *hierarchy; ++ struct net_shaper *cur; + + hierarchy = net_shaper_hierarchy_rcu(binding); +- if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, +- NET_SHAPER_VALID)) ++ if (!hierarchy) + return NULL; + +- /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is +- * valid, its contents must be visible too. +- */ +- smp_rmb(); +- return xa_load(&hierarchy->shapers, index); ++ cur = xa_load(&hierarchy->shapers, index); ++ /* Check valid before reading fields */ ++ if (!cur || !smp_load_acquire(&cur->valid)) ++ return NULL; ++ ++ return cur; + } + + /* Allocate on demand the per device shaper's hierarchy container. +@@ -444,12 +437,10 @@ static void net_shaper_commit(struct net_shaper_binding *binding, + if (WARN_ON_ONCE(!cur)) + continue; + +- /* Successful update: drop the tentative mark +- * and update the hierarchy container. +- */ ++ /* Successful update: update the hierarchy container... */ + net_shaper_copy(cur, &shapers[i]); +- smp_wmb(); +- __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); ++ /* ... publish to lockless readers. */ ++ smp_store_release(&cur->valid, true); + } + xa_unlock(&hierarchy->shapers); + } +@@ -466,10 +457,10 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) + + xa_lock(&hierarchy->shapers); + xa_for_each(&hierarchy->shapers, index, cur) { +- if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) ++ if (cur->valid) + continue; + __xa_erase(&hierarchy->shapers, index); +- kfree(cur); ++ kfree_rcu(cur, rcu); + } + xa_unlock(&hierarchy->shapers); + } +@@ -882,12 +873,12 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, + goto out_unlock; + + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, +- U32_MAX, NET_SHAPER_VALID)); ++ U32_MAX, XA_PRESENT)); + ctx->start_index++) { +- /* Pairs with smp_wmb() in net_shaper_commit(): the entry +- * is marked VALID, so its contents must be visible too. +- */ +- smp_rmb(); ++ /* Check valid before reading fields */ ++ if (!smp_load_acquire(&shaper->valid)) ++ continue; ++ + ret = net_shaper_fill_one(skb, binding, shaper, info); + if (ret) + break; +-- +2.53.0 + diff --git a/queue-7.0/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch b/queue-7.0/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch new file mode 100644 index 0000000000..d9087ad07e --- /dev/null +++ b/queue-7.0/net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch @@ -0,0 +1,41 @@ +From 9a533711fad1a0d2897b79896b62613e63fe3bc0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 12:28:59 -0700 +Subject: net: shaper: set ret to -ENOMEM when genlmsg_new() fails in + group_doit + +From: Jakub Kicinski + +[ Upstream commit 8054f85b83f42a37d482fc77ea7c9ff06a9407d9 ] + +genlmsg_new() alloc failure path in net_shaper_nl_group_doit() forgets +to set ret before jumping to error handling. + +Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/shaper/shaper.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c +index c8960821cf236..12e5e0c18643b 100644 +--- a/net/shaper/shaper.c ++++ b/net/shaper/shaper.c +@@ -1276,8 +1276,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) + * rollback on allocation failure. + */ + msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); +- if (!msg) ++ if (!msg) { ++ ret = -ENOMEM; + goto free_leaves; ++ } + + hierarchy = net_shaper_hierarchy_setup(binding); + if (!hierarchy) { +-- +2.53.0 + diff --git a/queue-7.0/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch b/queue-7.0/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch new file mode 100644 index 0000000000..df1de101df --- /dev/null +++ b/queue-7.0/net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch @@ -0,0 +1,65 @@ +From b6b57191df31c014994efcb42f16a576438dd496 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 15:26:40 -0700 +Subject: net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint + +From: Xiang Mei + +[ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] + +The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and +smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: + + __string(name, smc->conn.lnk->ibname) + +conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on +these paths already handles this (e.g. !conn->lnk in +SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first +sendmsg()/recvmsg() on an SMC-D socket crashes: + + Oops: general protection fault, probably for non-canonical address + KASAN: null-ptr-deref in range [...] + RIP: 0010:strlen+0x1e/0xa0 + Call Trace: + trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) + smc_rx_recvmsg (net/smc/smc_rx.c:515) + smc_recvmsg (net/smc/af_smc.c:2859) + __sys_recvfrom (net/socket.c:2315) + __x64_sys_recvfrom (net/socket.c:2326) + do_syscall_64 + +The faulting address 0x3e0 is offsetof(struct smc_link, ibname), +confirming the NULL ->lnk deref. Enabling the tracepoint requires +root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has +no capability check, and SMC-D negotiation needs no admin step on +s390 or on x86 with the loopback ISM device loaded. + +Log an empty device name for SMC-D instead of dereferencing NULL. + +Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Dust Li +Reviewed-by: Sidraya Jayagond +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/smc/smc_tracepoint.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h +index a9a6e3c1113aa..53da84f57fd6f 100644 +--- a/net/smc/smc_tracepoint.h ++++ b/net/smc/smc_tracepoint.h +@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, + __field(const void *, smc) + __field(u64, net_cookie) + __field(size_t, len) +- __string(name, smc->conn.lnk->ibname) ++ __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") + ), + + TP_fast_assign( +-- +2.53.0 + diff --git a/queue-7.0/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch b/queue-7.0/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch new file mode 100644 index 0000000000..1effb1b707 --- /dev/null +++ b/queue-7.0/net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch @@ -0,0 +1,63 @@ +From 9780be2e426a8ed8c1bebf8c5061c13b33ea7bf1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 23:21:38 -0700 +Subject: net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot + +From: Xiang Mei + +[ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] + +On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is +reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() +populates V2 entries starting at index 1, so when no V1 device is +selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == +NULL and ism_chid[0] == 0. + +smc_v2_determine_accepted_chid() then matches the peer's CHID against +the array starting from index 0 using the CHID alone. A malicious +peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches +the empty slot, ini->ism_selected becomes 0, and the subsequent +ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at +offsetof(struct smcd_dev, lgr_lock) == 0x68: + + BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 + Write of size 4 at addr 0000000000000068 by task exploit/144 + Call Trace: + _raw_spin_lock_bh + smc_conn_create (net/smc/smc_core.c:1997) + __smc_connect (net/smc/af_smc.c:1447) + smc_connect (net/smc/af_smc.c:1720) + __sys_connect + __x64_sys_connect + do_syscall_64 + +Require ism_dev[i] to be non-NULL before accepting a CHID match. + +Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") +Reported-by: Weiming Shi +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Xiang Mei +Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/smc/af_smc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c +index 1a565095376aa..f744f79112177 100644 +--- a/net/smc/af_smc.c ++++ b/net/smc/af_smc.c +@@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, + int i; + + for (i = 0; i < ini->ism_offered_cnt + 1; i++) { +- if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ++ if (ini->ism_dev[i] && ++ ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + ini->ism_selected = i; + return 0; + } +-- +2.53.0 + diff --git a/queue-7.0/net-stmmac-eswin-clear-txd-and-rxd-delay-registers-d.patch b/queue-7.0/net-stmmac-eswin-clear-txd-and-rxd-delay-registers-d.patch new file mode 100644 index 0000000000..0febf86edc --- /dev/null +++ b/queue-7.0/net-stmmac-eswin-clear-txd-and-rxd-delay-registers-d.patch @@ -0,0 +1,86 @@ +From f802bfb721fe730f0ccc61a0911c12e6b04c4676 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:21:37 +0800 +Subject: net: stmmac: eswin: clear TXD and RXD delay registers during + initialization + +From: Zhi Li + +[ Upstream commit 6872fb088edc1a3c36792b301f8e4a1c35dd7c35 ] + +Clear the TXD and RXD delay control registers during EIC7700 DWMAC +initialization. + +These registers may retain values programmed by the bootloader. If left +unchanged, residual delays can alter the effective RGMII timing seen by +the MAC and override the configuration described by the device tree. + +This may violate the expected RGMII timing model and can cause link +instability or prevent the Ethernet controller from operating correctly. + +Explicitly clearing these registers ensures that the MAC delay settings +are determined solely by the kernel configuration. + +The corresponding register offsets are optional, and the registers are +only cleared when the offsets are provided in the device tree. + +Fixes: ea77dbbdbc4e ("net: stmmac: add Eswin EIC7700 glue driver") +Signed-off-by: Zhi Li +Link: https://patch.msgid.link/20260518022137.464-1-lizhi2@eswincomputing.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../ethernet/stmicro/stmmac/dwmac-eic7700.c | 22 +++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +index 63001c4acdb7a..541b279f08a17 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +@@ -46,7 +46,11 @@ struct eic7700_qos_priv { + u32 eth_axi_lp_ctrl_offset; + u32 eth_phy_ctrl_offset; + u32 eth_clk_offset; ++ u32 eth_txd_offset; ++ u32 eth_rxd_offset; + u32 eth_clk_dly_param; ++ bool has_txd_offset; ++ bool has_rxd_offset; + }; + + static int eic7700_clks_config(void *priv, bool enabled) +@@ -84,6 +88,12 @@ static int eic7700_dwmac_init(struct device *dev, void *priv) + regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_axi_lp_ctrl_offset, + EIC7700_ETH_CSYSREQ_VAL); + ++ if (dwc->has_txd_offset) ++ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_txd_offset, 0); ++ ++ if (dwc->has_rxd_offset) ++ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_rxd_offset, 0); ++ + regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_clk_offset, + dwc->eth_clk_dly_param); + +@@ -190,6 +200,18 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + return dev_err_probe(&pdev->dev, ret, + "can't get eth_clk_offset\n"); + ++ ret = of_property_read_u32_index(pdev->dev.of_node, ++ "eswin,hsp-sp-csr", ++ 4, &dwc_priv->eth_txd_offset); ++ if (!ret) ++ dwc_priv->has_txd_offset = true; ++ ++ ret = of_property_read_u32_index(pdev->dev.of_node, ++ "eswin,hsp-sp-csr", ++ 5, &dwc_priv->eth_rxd_offset); ++ if (!ret) ++ dwc_priv->has_rxd_offset = true; ++ + plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names); + plat_dat->clks = devm_kcalloc(&pdev->dev, + plat_dat->num_clks, +-- +2.53.0 + diff --git a/queue-7.0/net-stmmac-eswin-correct-rgmii-delay-granularity-to-.patch b/queue-7.0/net-stmmac-eswin-correct-rgmii-delay-granularity-to-.patch new file mode 100644 index 0000000000..6bc4acd100 --- /dev/null +++ b/queue-7.0/net-stmmac-eswin-correct-rgmii-delay-granularity-to-.patch @@ -0,0 +1,64 @@ +From a1eff9f44780e22c59cfa25e70fe37cd9197a039 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:21:52 +0800 +Subject: net: stmmac: eswin: correct RGMII delay granularity to 20 ps + +From: Zhi Li + +[ Upstream commit 6ffcef9bc1fc2ad8110777decd6d026e3cb468ce ] + +The EIC7700 MAC implements programmable RGMII delay adjustment with a +granularity of 20 ps per hardware step. + +The driver previously converted rx-internal-delay-ps and +tx-internal-delay-ps values using a 100 ps step size, resulting in +incorrect delay programming. + +Update the conversion to use the correct 20 ps granularity so the +programmed delay matches the values described in the device tree. + +Fixes: ea77dbbdbc4e ("net: stmmac: add Eswin EIC7700 glue driver") +Signed-off-by: Zhi Li +Link: https://patch.msgid.link/20260518022156.484-1-lizhi2@eswincomputing.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +index 541b279f08a17..ef60cab24533e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +@@ -28,8 +28,8 @@ + + /* + * TX/RX Clock Delay Bit Masks: +- * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.1ns per bit) +- * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.1ns per bit) ++ * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.02ns per bit) ++ * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.02ns per bit) + */ + #define EIC7700_ETH_TX_ADJ_DELAY GENMASK(14, 8) + #define EIC7700_ETH_RX_ADJ_DELAY GENMASK(30, 24) +@@ -148,7 +148,7 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + /* Read rx-internal-delay-ps and update rx_clk delay */ + if (!of_property_read_u32(pdev->dev.of_node, + "rx-internal-delay-ps", &delay_ps)) { +- u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); ++ u32 val = min(delay_ps / 20, EIC7700_MAX_DELAY_UNIT); + + dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY; + dwc_priv->eth_clk_dly_param |= +@@ -161,7 +161,7 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + /* Read tx-internal-delay-ps and update tx_clk delay */ + if (!of_property_read_u32(pdev->dev.of_node, + "tx-internal-delay-ps", &delay_ps)) { +- u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); ++ u32 val = min(delay_ps / 20, EIC7700_MAX_DELAY_UNIT); + + dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY; + dwc_priv->eth_clk_dly_param |= +-- +2.53.0 + diff --git a/queue-7.0/net-stmmac-eswin-fix-hsp-csr-init-ordering-after-clo.patch b/queue-7.0/net-stmmac-eswin-fix-hsp-csr-init-ordering-after-clo.patch new file mode 100644 index 0000000000..8c99fe3192 --- /dev/null +++ b/queue-7.0/net-stmmac-eswin-fix-hsp-csr-init-ordering-after-clo.patch @@ -0,0 +1,172 @@ +From 89cb3b1a6b21cfe0f9a105ee4b2c5012c7868347 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:20:55 +0800 +Subject: net: stmmac: eswin: fix HSP CSR init ordering after clock enable + +From: Zhi Li + +[ Upstream commit 23386defe949c0db4f746bed7098fc5e06746083 ] + +Fix the initialization ordering of the HSP CSR configuration in the +EIC7700 DWMAC glue driver. + +The HSP CSR registers control MAC-side RGMII delay behavior and must +only be accessed after the corresponding clocks are enabled. The +previous implementation could trigger register access before clock +enablement, leading to undefined behavior depending on boot state. + +Move the HSP CSR configuration into the post-clock-enable initialization +path to ensure all register accesses occur under valid clock domains. + +This change ensures deterministic initialization and prevents +clock-dependent register access failures during probe or resume. + +Fixes: ea77dbbdbc4e ("net: stmmac: add Eswin EIC7700 glue driver") +Signed-off-by: Zhi Li +Link: https://patch.msgid.link/20260518022055.444-1-lizhi2@eswincomputing.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../ethernet/stmicro/stmmac/dwmac-eic7700.c | 73 +++++++++++-------- + 1 file changed, 41 insertions(+), 32 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +index bcb8e000e720b..63001c4acdb7a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +@@ -42,6 +42,11 @@ static const char * const eic7700_clk_names[] = { + + struct eic7700_qos_priv { + struct plat_stmmacenet_data *plat_dat; ++ struct regmap *eic7700_hsp_regmap; ++ u32 eth_axi_lp_ctrl_offset; ++ u32 eth_phy_ctrl_offset; ++ u32 eth_clk_offset; ++ u32 eth_clk_dly_param; + }; + + static int eic7700_clks_config(void *priv, bool enabled) +@@ -61,8 +66,28 @@ static int eic7700_clks_config(void *priv, bool enabled) + static int eic7700_dwmac_init(struct device *dev, void *priv) + { + struct eic7700_qos_priv *dwc = priv; ++ int ret; ++ ++ ret = eic7700_clks_config(dwc, true); ++ if (ret) ++ return ret; ++ ++ ret = regmap_set_bits(dwc->eic7700_hsp_regmap, ++ dwc->eth_phy_ctrl_offset, ++ EIC7700_ETH_TX_CLK_SEL | ++ EIC7700_ETH_PHY_INTF_SELI); ++ if (ret) { ++ eic7700_clks_config(dwc, false); ++ return ret; ++ } ++ ++ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_axi_lp_ctrl_offset, ++ EIC7700_ETH_CSYSREQ_VAL); + +- return eic7700_clks_config(dwc, true); ++ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_clk_offset, ++ dwc->eth_clk_dly_param); ++ ++ return 0; + } + + static void eic7700_dwmac_exit(struct device *dev, void *priv) +@@ -93,12 +118,6 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct eic7700_qos_priv *dwc_priv; +- struct regmap *eic7700_hsp_regmap; +- u32 eth_axi_lp_ctrl_offset; +- u32 eth_phy_ctrl_offset; +- u32 eth_phy_ctrl_regset; +- u32 eth_rxd_dly_offset; +- u32 eth_dly_param = 0; + u32 delay_ps; + int i, ret; + +@@ -121,8 +140,9 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + "rx-internal-delay-ps", &delay_ps)) { + u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); + +- eth_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY; +- eth_dly_param |= FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val); ++ dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY; ++ dwc_priv->eth_clk_dly_param |= ++ FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val); + } else { + return dev_err_probe(&pdev->dev, -EINVAL, + "missing required property rx-internal-delay-ps\n"); +@@ -133,53 +153,42 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + "tx-internal-delay-ps", &delay_ps)) { + u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); + +- eth_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY; +- eth_dly_param |= FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val); ++ dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY; ++ dwc_priv->eth_clk_dly_param |= ++ FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val); + } else { + return dev_err_probe(&pdev->dev, -EINVAL, + "missing required property tx-internal-delay-ps\n"); + } + +- eic7700_hsp_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, +- "eswin,hsp-sp-csr"); +- if (IS_ERR(eic7700_hsp_regmap)) ++ dwc_priv->eic7700_hsp_regmap = ++ syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "eswin,hsp-sp-csr"); ++ if (IS_ERR(dwc_priv->eic7700_hsp_regmap)) + return dev_err_probe(&pdev->dev, +- PTR_ERR(eic7700_hsp_regmap), ++ PTR_ERR(dwc_priv->eic7700_hsp_regmap), + "Failed to get hsp-sp-csr regmap\n"); + + ret = of_property_read_u32_index(pdev->dev.of_node, + "eswin,hsp-sp-csr", +- 1, ð_phy_ctrl_offset); ++ 1, &dwc_priv->eth_phy_ctrl_offset); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "can't get eth_phy_ctrl_offset\n"); + +- regmap_read(eic7700_hsp_regmap, eth_phy_ctrl_offset, +- ð_phy_ctrl_regset); +- eth_phy_ctrl_regset |= +- (EIC7700_ETH_TX_CLK_SEL | EIC7700_ETH_PHY_INTF_SELI); +- regmap_write(eic7700_hsp_regmap, eth_phy_ctrl_offset, +- eth_phy_ctrl_regset); +- + ret = of_property_read_u32_index(pdev->dev.of_node, + "eswin,hsp-sp-csr", +- 2, ð_axi_lp_ctrl_offset); ++ 2, &dwc_priv->eth_axi_lp_ctrl_offset); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "can't get eth_axi_lp_ctrl_offset\n"); + +- regmap_write(eic7700_hsp_regmap, eth_axi_lp_ctrl_offset, +- EIC7700_ETH_CSYSREQ_VAL); +- + ret = of_property_read_u32_index(pdev->dev.of_node, + "eswin,hsp-sp-csr", +- 3, ð_rxd_dly_offset); ++ 3, &dwc_priv->eth_clk_offset); + if (ret) + return dev_err_probe(&pdev->dev, ret, +- "can't get eth_rxd_dly_offset\n"); +- +- regmap_write(eic7700_hsp_regmap, eth_rxd_dly_offset, +- eth_dly_param); ++ "can't get eth_clk_offset\n"); + + plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names); + plat_dat->clks = devm_kcalloc(&pdev->dev, +-- +2.53.0 + diff --git a/queue-7.0/net-stmmac-eswin-validate-rgmii-delay-values.patch b/queue-7.0/net-stmmac-eswin-validate-rgmii-delay-values.patch new file mode 100644 index 0000000000..5ce3307148 --- /dev/null +++ b/queue-7.0/net-stmmac-eswin-validate-rgmii-delay-values.patch @@ -0,0 +1,95 @@ +From d9d7f33601235d0b617a3d9c64af02b65b2ff84f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 10:22:13 +0800 +Subject: net: stmmac: eswin: validate RGMII delay values + +From: Zhi Li + +[ Upstream commit c2e152f7ce3208b9333d212d41a87637ec1dd170 ] + +Validate rx-internal-delay-ps and tx-internal-delay-ps against the +hardware capabilities of the EIC7700 MAC. + +The programmable RGMII delay supports 20 ps steps and a maximum value of +2540 ps. The driver previously accepted arbitrary values and silently +truncated unsupported settings when converting them to hardware units. + +As a result, invalid device tree values could lead to unexpected delay +programming and incorrect RGMII timing. + +Reject delay values that are not multiples of 20 ps or exceed the +supported hardware range. + +Fixes: ea77dbbdbc4e ("net: stmmac: add Eswin EIC7700 glue driver") +Signed-off-by: Zhi Li +Link: https://patch.msgid.link/20260518022214.507-1-lizhi2@eswincomputing.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + .../ethernet/stmicro/stmmac/dwmac-eic7700.c | 29 ++++++++++++++++--- + 1 file changed, 25 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +index ef60cab24533e..4ac979d874d6e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c +@@ -34,7 +34,10 @@ + #define EIC7700_ETH_TX_ADJ_DELAY GENMASK(14, 8) + #define EIC7700_ETH_RX_ADJ_DELAY GENMASK(30, 24) + +-#define EIC7700_MAX_DELAY_UNIT 0x7F ++#define EIC7700_MAX_DELAY_STEPS 0x7F ++#define EIC7700_DELAY_STEP_PS 20 ++#define EIC7700_MAX_DELAY_PS \ ++ (EIC7700_MAX_DELAY_STEPS * EIC7700_DELAY_STEP_PS) + + static const char * const eic7700_clk_names[] = { + "tx", "axi", "cfg", +@@ -128,7 +131,7 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct eic7700_qos_priv *dwc_priv; +- u32 delay_ps; ++ u32 delay_ps, val; + int i, ret; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); +@@ -148,7 +151,16 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + /* Read rx-internal-delay-ps and update rx_clk delay */ + if (!of_property_read_u32(pdev->dev.of_node, + "rx-internal-delay-ps", &delay_ps)) { +- u32 val = min(delay_ps / 20, EIC7700_MAX_DELAY_UNIT); ++ if (delay_ps % EIC7700_DELAY_STEP_PS) ++ return dev_err_probe(&pdev->dev, -EINVAL, ++ "rx delay must be multiple of %dps\n", ++ EIC7700_DELAY_STEP_PS); ++ ++ if (delay_ps > EIC7700_MAX_DELAY_PS) ++ return dev_err_probe(&pdev->dev, -EINVAL, ++ "rx delay out of range\n"); ++ ++ val = delay_ps / EIC7700_DELAY_STEP_PS; + + dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY; + dwc_priv->eth_clk_dly_param |= +@@ -161,7 +173,16 @@ static int eic7700_dwmac_probe(struct platform_device *pdev) + /* Read tx-internal-delay-ps and update tx_clk delay */ + if (!of_property_read_u32(pdev->dev.of_node, + "tx-internal-delay-ps", &delay_ps)) { +- u32 val = min(delay_ps / 20, EIC7700_MAX_DELAY_UNIT); ++ if (delay_ps % EIC7700_DELAY_STEP_PS) ++ return dev_err_probe(&pdev->dev, -EINVAL, ++ "tx delay must be multiple of %dps\n", ++ EIC7700_DELAY_STEP_PS); ++ ++ if (delay_ps > EIC7700_MAX_DELAY_PS) ++ return dev_err_probe(&pdev->dev, -EINVAL, ++ "tx delay out of range\n"); ++ ++ val = delay_ps / EIC7700_DELAY_STEP_PS; + + dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY; + dwc_priv->eth_clk_dly_param |= +-- +2.53.0 + diff --git a/queue-7.0/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch b/queue-7.0/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch new file mode 100644 index 0000000000..abe62bf152 --- /dev/null +++ b/queue-7.0/net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch @@ -0,0 +1,39 @@ +From 38bd4176a3172451766fc0884f0af19ad50e3b94 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 01:28:13 +0530 +Subject: net: ti: icssm-prueth: fix eth_ports_node leak in probe + +From: Shitalkumar Gandhi + +[ Upstream commit 6635fa84403c3a59455b66007c019a7cc632db30 ] + +The error path on of_property_read_u32() failure inside +icssm_prueth_probe() returns without putting eth_ports_node, +which was acquired before the for_each_child_of_node() loop. + +Drop it before returning. + +Fixes: 511f6c1ae093 ("net: ti: icssm-prueth: Adds ICSSM Ethernet driver") +Signed-off-by: Shitalkumar Gandhi +Link: https://patch.msgid.link/20260506195813.641610-1-shitalkumar.gandhi@cambiumnetworks.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/ti/icssm/icssm_prueth.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c +index 53bbd92909042..b7e94244355a3 100644 +--- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c ++++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c +@@ -1825,6 +1825,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) + dev_err(dev, "%pOF error reading port_id %d\n", + eth_node, ret); + of_node_put(eth_node); ++ of_node_put(eth_ports_node); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch b/queue-7.0/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch new file mode 100644 index 0000000000..de3665d99b --- /dev/null +++ b/queue-7.0/net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch @@ -0,0 +1,80 @@ +From 1c37e6d4896bd46740ad7c8890b972a7b7e2aa31 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:17 -0700 +Subject: net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg + ring +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jakub Kicinski + +[ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] + +When an sk_msg scatterlist ring wraps (sg.end < sg.start), +tls_push_record() chains the tail portion of the ring to the head +using sg_chain(). An extra entry in the sg array is reserved for +this: + + struct sk_msg_sg { + [...] + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the + * chaining; + * 2) to chain tailer SG entries after the message. + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + +The current code uses MAX_SKB_FRAGS + 1 as the ring size: + + sg_chain(&msg_pl->sg.data[msg_pl->sg.start], + MAX_SKB_FRAGS - msg_pl->sg.start + 1, + msg_pl->sg.data); + +This places the chain pointer at + + sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = + &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = + data[start + (MAX_SKB_FRAGS - start + 1) - 1] = + data[MAX_SKB_FRAGS] + +instead of the true last entry. This is likely due to a "race" of +the commit under Fixes landing close to +commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") + +Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested +by Sabrina). + +Reported-by: 钱一铭 +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Reviewed-by: Sabrina Dubroca +Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 23a31646d0387..fe73c6da73392 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -800,11 +800,9 @@ static int tls_push_record(struct sock *sk, int flags, + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) { +- sg_chain(&msg_pl->sg.data[msg_pl->sg.start], +- MAX_SKB_FRAGS - msg_pl->sg.start + 1, ++ if (msg_pl->sg.end < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); +- } + + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); +-- +2.53.0 + diff --git a/queue-7.0/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch b/queue-7.0/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch new file mode 100644 index 0000000000..2afb9349be --- /dev/null +++ b/queue-7.0/net-tls-prevent-chain-after-chain-in-plain-text-sg.patch @@ -0,0 +1,93 @@ +From 115e43fff117cdffa7a8a3e1199d5f10ff1b28c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 10:49:18 -0700 +Subject: net: tls: prevent chain-after-chain in plain text SG + +From: Jakub Kicinski + +[ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] + +Sashiko points out that if end = 0 (start != 0) the current +code will create a chain link to content type right after +the wrap link: + + This would create a chain where the wrap link points directly + to another chain link. The scatterlist API sg_next iterator + does not recursively resolve consecutive chain links. + +meaning this is illegal input to crypto. + +The wrapping link is unnecessary if end = 0. end is the entry after +the last one used so end = 0 means there's nothing pushed after +the wrap: + + end start i + v v v + [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] + +Skip the wrapping in this case. + +TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. +This avoids the chain-after-chain. + +Move the wrap chaining before marking END and chaining off content +type, that feels like more logical ordering to me, but should not +matter from functional perspective. + +Reported-by: Sashiko +Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") +Signed-off-by: Jakub Kicinski +Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index fe73c6da73392..97e02ac7f0086 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -789,21 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, + i = msg_pl->sg.end; + sk_msg_iter_var_prev(i); + ++ /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap ++ * link (frags won't use it). 'i' is now the last filled entry: ++ * ++ * i end start ++ * v v v [ rsv ] ++ * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] ++ * ^ END v ++ * `-----------------------------------------' ++ * ++ * Note that SGL does not allow chain-after-chain, so for TLS 1.3, ++ * we must make sure we don't create the wrap entry and then chain ++ * link to content_type immediately at index 0. ++ */ ++ if (i < msg_pl->sg.start) ++ sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), ++ msg_pl->sg.data); ++ + rec->content_type = record_type; + if (prot->version == TLS_1_3_VERSION) { + /* Add content type to end of message. No padding added */ + sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); + sg_mark_end(&rec->sg_content_type); +- sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, +- &rec->sg_content_type); ++ sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); + } else { + sg_mark_end(sk_msg_elem(msg_pl, i)); + } + +- if (msg_pl->sg.end < msg_pl->sg.start) +- sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), +- msg_pl->sg.data); +- + i = msg_pl->sg.start; + sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); + +-- +2.53.0 + diff --git a/queue-7.0/netfilter-bridge-eb_tables-close-module-init-race.patch b/queue-7.0/netfilter-bridge-eb_tables-close-module-init-race.patch new file mode 100644 index 0000000000..4f7fe9d659 --- /dev/null +++ b/queue-7.0/netfilter-bridge-eb_tables-close-module-init-race.patch @@ -0,0 +1,56 @@ +From ec05f736b2ab53484c52f2546aa3385e67c55e88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 11:19:22 +0200 +Subject: netfilter: bridge: eb_tables: close module init race + +From: Florian Westphal + +[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] + +sashiko reports for unrelated patch: + Does the core ebtables initialization in ebtables.c suffer from a similar race? + Once nf_register_sockopt() completes, the sockopts are exposed globally. + +sockopt has to be registered last, just like in ip/ip6/arptables. + +Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtables.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index 3578ffbc14aee..b9f4daac09af3 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) + { + int ret; + +- ret = xt_register_target(&ebt_standard_target); ++ ret = register_pernet_subsys(&ebt_net_ops); + if (ret < 0) + return ret; +- ret = nf_register_sockopt(&ebt_sockopts); ++ ++ ret = xt_register_target(&ebt_standard_target); + if (ret < 0) { +- xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +- ret = register_pernet_subsys(&ebt_net_ops); ++ ret = nf_register_sockopt(&ebt_sockopts); + if (ret < 0) { +- nf_unregister_sockopt(&ebt_sockopts); + xt_unregister_target(&ebt_standard_target); ++ unregister_pernet_subsys(&ebt_net_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/netfilter-ebtables-close-dangling-table-module-init-.patch b/queue-7.0/netfilter-ebtables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..f565817e7f --- /dev/null +++ b/queue-7.0/netfilter-ebtables-close-dangling-table-module-init-.patch @@ -0,0 +1,116 @@ +From e3e7add162ad53e6d66c5022c60183e89b33ccc5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:19 +0200 +Subject: netfilter: ebtables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] + +sashiko reported for a related patch: + In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, + the rollback might call kfree(rawtable_ops) before [..] + During this window, could a concurrent userspace process find the globally + visible template, trigger table_init(), [..] + +The table init functions must always register the template last. + +Otherwise, set/getsockopt can instantiate a table in a namespace +while the required pernet ops (contain the destructor) isn't available. +This change is also required in x_tables, handled in followup change. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 12 +++++------- + net/bridge/netfilter/ebtable_filter.c | 12 +++++------- + net/bridge/netfilter/ebtable_nat.c | 10 ++++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index e6f9e343b41f1..f05c79f215ea0 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { + + static int __init ebtable_broute_init(void) + { +- int ret = ebt_register_template(&broute_table, broute_table_init); ++ int ret = register_pernet_subsys(&broute_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&broute_net_ops); +- if (ret) { +- ebt_unregister_template(&broute_table); +- return ret; +- } ++ ret = ebt_register_template(&broute_table, broute_table_init); ++ if (ret) ++ unregister_pernet_subsys(&broute_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_broute_fini(void) +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index 02b6501c15a5e..0fc03b07e62ae 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { + + static int __init ebtable_filter_init(void) + { +- int ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ int ret = register_pernet_subsys(&frame_filter_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_filter_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_filter); +- return ret; +- } ++ ret = ebt_register_template(&frame_filter, frame_filter_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_filter_net_ops); + +- return 0; ++ return ret; + } + + static void __exit ebtable_filter_fini(void) +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 9985a82555c41..8a10375d89099 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { + + static int __init ebtable_nat_init(void) + { +- int ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ int ret = register_pernet_subsys(&frame_nat_net_ops); + + if (ret) + return ret; + +- ret = register_pernet_subsys(&frame_nat_net_ops); +- if (ret) { +- ebt_unregister_template(&frame_nat); +- return ret; +- } ++ ret = ebt_register_template(&frame_nat, frame_nat_table_init); ++ if (ret) ++ unregister_pernet_subsys(&frame_nat_net_ops); + + return ret; + } +-- +2.53.0 + diff --git a/queue-7.0/netfilter-ebtables-move-to-two-stage-removal-scheme.patch b/queue-7.0/netfilter-ebtables-move-to-two-stage-removal-scheme.patch new file mode 100644 index 0000000000..e71b73ddd5 --- /dev/null +++ b/queue-7.0/netfilter-ebtables-move-to-two-stage-removal-scheme.patch @@ -0,0 +1,197 @@ +From d20e516586eb183700ba5f780af1e2dda6d5f487 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:18 +0200 +Subject: netfilter: ebtables: move to two-stage removal scheme + +From: Florian Westphal + +[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] + +Like previous patches for x_tables, follow same pattern in ebtables. +We can't reuse xt helpers: ebt_table struct layout is incompatible. + +table->ops assignment is now done while still holding the ebt mutex +to make sure we never expose partially-filled table struct. + +Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/bridge/netfilter/ebtable_broute.c | 2 +- + net/bridge/netfilter/ebtable_filter.c | 2 +- + net/bridge/netfilter/ebtable_nat.c | 2 +- + net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- + 4 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c +index 7413602195525..e6f9e343b41f1 100644 +--- a/net/bridge/netfilter/ebtable_broute.c ++++ b/net/bridge/netfilter/ebtable_broute.c +@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) + + static void __exit ebtable_broute_fini(void) + { +- unregister_pernet_subsys(&broute_net_ops); + ebt_unregister_template(&broute_table); ++ unregister_pernet_subsys(&broute_net_ops); + } + + module_init(ebtable_broute_init); +diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c +index dacd81b12e626..02b6501c15a5e 100644 +--- a/net/bridge/netfilter/ebtable_filter.c ++++ b/net/bridge/netfilter/ebtable_filter.c +@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) + + static void __exit ebtable_filter_fini(void) + { +- unregister_pernet_subsys(&frame_filter_net_ops); + ebt_unregister_template(&frame_filter); ++ unregister_pernet_subsys(&frame_filter_net_ops); + } + + module_init(ebtable_filter_init); +diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c +index 0f2a8c6118d42..9985a82555c41 100644 +--- a/net/bridge/netfilter/ebtable_nat.c ++++ b/net/bridge/netfilter/ebtable_nat.c +@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) + + static void __exit ebtable_nat_fini(void) + { +- unregister_pernet_subsys(&frame_nat_net_ops); + ebt_unregister_template(&frame_nat); ++ unregister_pernet_subsys(&frame_nat_net_ops); + } + + module_init(ebtable_nat_init); +diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c +index aea3e19875c69..3578ffbc14aee 100644 +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -42,6 +42,7 @@ + + struct ebt_pernet { + struct list_head tables; ++ struct list_head dead_tables; + }; + + struct ebt_template { +@@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) + + static void __ebt_unregister_table(struct net *net, struct ebt_table *table) + { +- mutex_lock(&ebt_mutex); +- list_del(&table->list); +- mutex_unlock(&ebt_mutex); +- audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) +@@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + +- list_add(&table->list, &ebt_net->tables); +- mutex_unlock(&ebt_mutex); +- + table->ops = ops; + ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret) ++ if (ret) { ++ synchronize_rcu(); + __ebt_unregister_table(net, table); ++ } else { ++ list_add(&table->list, &ebt_net->tables); ++ } ++ mutex_unlock(&ebt_mutex); + + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); +@@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) + } + EXPORT_SYMBOL(ebt_unregister_template); + +-static struct ebt_table *__ebt_find_table(struct net *net, const char *name) ++void ebt_unregister_table_pre_exit(struct net *net, const char *name) + { + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; +@@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) + + list_for_each_entry(t, &ebt_net->tables, list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &ebt_net->dead_tables); + mutex_unlock(&ebt_mutex); +- return t; ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; + } + } + + mutex_unlock(&ebt_mutex); +- return NULL; +-} +- +-void ebt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct ebt_table *table = __ebt_find_table(net, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); + } + EXPORT_SYMBOL(ebt_unregister_table_pre_exit); + + void ebt_unregister_table(struct net *net, const char *name) + { +- struct ebt_table *table = __ebt_find_table(net, name); ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ struct ebt_table *t; + +- if (table) +- __ebt_unregister_table(net, table); ++ mutex_lock(&ebt_mutex); ++ ++ list_for_each_entry(t, &ebt_net->dead_tables, list) { ++ if (strcmp(t->name, name) == 0) { ++ list_del(&t->list); ++ audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ __ebt_unregister_table(net, t); ++ mutex_unlock(&ebt_mutex); ++ return; ++ } ++ } ++ ++ mutex_unlock(&ebt_mutex); + } + + /* userspace just supplied us with counters */ +@@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + INIT_LIST_HEAD(&ebt_net->tables); ++ INIT_LIST_HEAD(&ebt_net->dead_tables); + return 0; + } + ++static void __net_exit ebt_pernet_exit(struct net *net) ++{ ++ struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); ++ ++ WARN_ON_ONCE(!list_empty(&ebt_net->tables)); ++ WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); ++} ++ + static struct pernet_operations ebt_net_ops = { + .init = ebt_pernet_init, ++ .exit = ebt_pernet_exit, + .id = &ebt_pernet_id, + .size = sizeof(struct ebt_pernet), + }; +-- +2.53.0 + diff --git a/queue-7.0/netfilter-nf_conntrack_expect-restore-helper-propaga.patch b/queue-7.0/netfilter-nf_conntrack_expect-restore-helper-propaga.patch new file mode 100644 index 0000000000..c309283d56 --- /dev/null +++ b/queue-7.0/netfilter-nf_conntrack_expect-restore-helper-propaga.patch @@ -0,0 +1,267 @@ +From 9826af9cb45e322f81550b3f98ce549a3f3539d6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 13:00:28 +0200 +Subject: netfilter: nf_conntrack_expect: restore helper propagation via + expectation + +From: Pablo Neira Ayuso + +[ Upstream commit dcb0f9aefdd604d36710fda53c25bd7cf4a3e37a ] + +A recent series to fix expectations broke helper propagation via +expectation, this mechanism is used by the sip and h323 helper. This +also propagates the conntrack helper to expected connections. I changed +semantics of exp->helper which now tells us the actual helper that +created the expectation. + +Add an explicit assign_helper field to expectations for this purpose +and update helpers to use it. + +Restore this feature for userspace conntrack helper via ctnetlink +nfqueue integration so it is again possible to attach a helper to an +expectation, where it makes sense. This is not restored via ctnetlink +expectation creation as there is no client for such feature. Use the +expectation layer 4 protocol number for the helper lookup for +consistency. + +Make sure the expectation using this helper propagation mechanism also +go away when the helper is unregistered. + +Fixes: 9c42bc9db90a ("netfilter: nf_conntrack_expect: honor expectation helper field") +Fixes: 917b61fa2042 ("netfilter: ctnetlink: ignore explicit helper on new expectations") +Reported-by: Ilya Maximets +Tested-by: Ilya Maximets +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_conntrack_expect.h | 5 ++++- + net/netfilter/nf_conntrack_broadcast.c | 1 + + net/netfilter/nf_conntrack_core.c | 7 +++++-- + net/netfilter/nf_conntrack_expect.c | 1 + + net/netfilter/nf_conntrack_h323_main.c | 12 ++++++------ + net/netfilter/nf_conntrack_helper.c | 5 +++++ + net/netfilter/nf_conntrack_netlink.c | 18 ++++++++++++++++-- + net/netfilter/nf_conntrack_sip.c | 2 +- + 8 files changed, 39 insertions(+), 12 deletions(-) + +diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h +index e9a8350e7ccfb..80f50fd0f7ad2 100644 +--- a/include/net/netfilter/nf_conntrack_expect.h ++++ b/include/net/netfilter/nf_conntrack_expect.h +@@ -45,9 +45,12 @@ struct nf_conntrack_expect { + void (*expectfn)(struct nf_conn *new, + struct nf_conntrack_expect *this); + +- /* Helper to assign to new connection */ ++ /* Helper that created this expectation */ + struct nf_conntrack_helper __rcu *helper; + ++ /* Helper to assign to new connection */ ++ struct nf_conntrack_helper __rcu *assign_helper; ++ + /* The conntrack of the master connection */ + struct nf_conn *master; + +diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c +index 4f39bf7c843f2..75e53fde6b297 100644 +--- a/net/netfilter/nf_conntrack_broadcast.c ++++ b/net/netfilter/nf_conntrack_broadcast.c +@@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, + exp->flags = NF_CT_EXPECT_PERMANENT; + exp->class = NF_CT_EXPECT_CLASS_DEFAULT; + rcu_assign_pointer(exp->helper, helper); ++ rcu_assign_pointer(exp->assign_helper, NULL); + write_pnet(&exp->net, net); + #ifdef CONFIG_NF_CONNTRACK_ZONES + exp->zone = ct->zone; +diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c +index 27ce5fda89937..b5ee274be7739 100644 +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -1814,14 +1814,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, + spin_lock_bh(&nf_conntrack_expect_lock); + exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl)); + if (exp) { ++ struct nf_conntrack_helper *assign_helper; ++ + /* Welcome, Mr. Bond. We've been expecting you... */ + __set_bit(IPS_EXPECTED_BIT, &ct->status); + /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ + ct->master = exp->master; +- if (exp->helper) { ++ assign_helper = rcu_dereference(exp->assign_helper); ++ if (assign_helper) { + help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); + if (help) +- rcu_assign_pointer(help->helper, exp->helper); ++ rcu_assign_pointer(help->helper, assign_helper); + } + + #ifdef CONFIG_NF_CONNTRACK_MARK +diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c +index 24d0576d84b7f..8e943efbdf0a5 100644 +--- a/net/netfilter/nf_conntrack_expect.c ++++ b/net/netfilter/nf_conntrack_expect.c +@@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, + helper = rcu_dereference(help->helper); + + rcu_assign_pointer(exp->helper, helper); ++ rcu_assign_pointer(exp->assign_helper, NULL); + write_pnet(&exp->net, net); + #ifdef CONFIG_NF_CONNTRACK_ZONES + exp->zone = ct->zone; +diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c +index 3f5c50455b716..b2fe6554b9cf4 100644 +--- a/net/netfilter/nf_conntrack_h323_main.c ++++ b/net/netfilter/nf_conntrack_h323_main.c +@@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, + &ct->tuplehash[!dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3, + IPPROTO_TCP, NULL, &port); +- rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245); ++ rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245); + + nathook = rcu_dereference(nfct_h323_nat_hook); + if (memcmp(&ct->tuplehash[dir].tuple.src.u3, +@@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb, + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), + &ct->tuplehash[!dir].tuple.src.u3, &addr, + IPPROTO_TCP, NULL, &port); +- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); ++ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + + nathook = rcu_dereference(nfct_h323_nat_hook); + if (memcmp(&ct->tuplehash[dir].tuple.src.u3, +@@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, + &ct->tuplehash[!dir].tuple.src.u3 : NULL, + &ct->tuplehash[!dir].tuple.dst.u3, + IPPROTO_TCP, NULL, &port); +- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); ++ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */ + + nathook = rcu_dereference(nfct_h323_nat_hook); +@@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), + &ct->tuplehash[!dir].tuple.src.u3, &addr, + IPPROTO_UDP, NULL, &port); +- rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras); ++ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras); + + if (nf_ct_expect_related(exp, 0) == 0) { + pr_debug("nf_ct_ras: expect RAS "); +@@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, + &ct->tuplehash[!dir].tuple.src.u3, &addr, + IPPROTO_TCP, NULL, &port); + exp->flags = NF_CT_EXPECT_PERMANENT; +- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); ++ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + + if (nf_ct_expect_related(exp, 0) == 0) { + pr_debug("nf_ct_ras: expect Q.931 "); +@@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, + &ct->tuplehash[!dir].tuple.src.u3, &addr, + IPPROTO_TCP, NULL, &port); + exp->flags = NF_CT_EXPECT_PERMANENT; +- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); ++ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + + if (nf_ct_expect_related(exp, 0) == 0) { + pr_debug("nf_ct_ras: expect Q.931 "); +diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c +index a715304a53d8c..b594cd244fe1d 100644 +--- a/net/netfilter/nf_conntrack_helper.c ++++ b/net/netfilter/nf_conntrack_helper.c +@@ -400,6 +400,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) + + this = rcu_dereference_protected(exp->helper, + lockdep_is_held(&nf_conntrack_expect_lock)); ++ if (this == me) ++ return true; ++ ++ this = rcu_dereference_protected(exp->assign_helper, ++ lockdep_is_held(&nf_conntrack_expect_lock)); + return this == me; + } + +diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c +index a20cd82446c54..e2f149aabbe82 100644 +--- a/net/netfilter/nf_conntrack_netlink.c ++++ b/net/netfilter/nf_conntrack_netlink.c +@@ -2636,6 +2636,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { + + static struct nf_conntrack_expect * + ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, ++ const struct nf_conntrack_helper *assign_helper, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask); + +@@ -2862,6 +2863,7 @@ static int + ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report) + { ++ struct nf_conntrack_helper *assign_helper = NULL; + struct nlattr *cda[CTA_EXPECT_MAX+1]; + struct nf_conntrack_tuple tuple, mask; + struct nf_conntrack_expect *exp; +@@ -2877,8 +2879,18 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, + if (err < 0) + return err; + ++ if (cda[CTA_EXPECT_HELP_NAME]) { ++ const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); ++ ++ assign_helper = __nf_conntrack_helper_find(helpname, ++ nf_ct_l3num(ct), ++ tuple.dst.protonum); ++ if (!assign_helper) ++ return -EOPNOTSUPP; ++ } ++ + exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, +- &tuple, &mask); ++ assign_helper, &tuple, &mask); + if (IS_ERR(exp)) + return PTR_ERR(exp); + +@@ -3517,6 +3529,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, + + static struct nf_conntrack_expect * + ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, ++ const struct nf_conntrack_helper *assign_helper, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask) + { +@@ -3570,6 +3583,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, + exp->zone = ct->zone; + #endif + rcu_assign_pointer(exp->helper, helper); ++ rcu_assign_pointer(exp->assign_helper, assign_helper); + exp->tuple = *tuple; + exp->mask.src.u3 = mask->src.u3; + exp->mask.src.u.all = mask->src.u.all; +@@ -3625,7 +3639,7 @@ ctnetlink_create_expect(struct net *net, + ct = nf_ct_tuplehash_to_ctrack(h); + + rcu_read_lock(); +- exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask); ++ exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask); + if (IS_ERR(exp)) { + err = PTR_ERR(exp); + goto err_rcu; +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 81534213f00f3..fd4326ee1aca8 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1384,7 +1384,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, + nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), + saddr, &daddr, proto, NULL, &port); + exp->timeout.expires = sip_timeout * HZ; +- rcu_assign_pointer(exp->helper, helper); ++ rcu_assign_pointer(exp->assign_helper, helper); + exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; + + hooks = rcu_dereference(nf_nat_sip_hooks); +-- +2.53.0 + diff --git a/queue-7.0/netfilter-nft_inner-release-local_lock-before-re-ena.patch b/queue-7.0/netfilter-nft_inner-release-local_lock-before-re-ena.patch new file mode 100644 index 0000000000..bee8fbae46 --- /dev/null +++ b/queue-7.0/netfilter-nft_inner-release-local_lock-before-re-ena.patch @@ -0,0 +1,39 @@ +From 3ccacb2ec192db09504d6489995b8b9b8ad90827 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 11:30:49 +0200 +Subject: netfilter: nft_inner: release local_lock before re-enabling softirqs + +From: Florian Westphal + +[ Upstream commit a6cb3ff979855f7f0ee9450a947fe8f96c2ba37a ] + +Quoting sashiko: + In the error path, local_bh_enable() is called before + local_unlock_nested_bh(). + +Fixes: ba36fada9ab4 ("netfilter: nft_inner: Use nested-BH locking for nft_pcpu_tun_ctx") +Signed-off-by: Florian Westphal +Reviewed-by: Fernando Fernandez Mancera +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_inner.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c +index 1b3e7a976f560..ad08a43535b55 100644 +--- a/net/netfilter/nft_inner.c ++++ b/net/netfilter/nft_inner.c +@@ -246,8 +246,8 @@ static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt, + local_lock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); + this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx.ctx); + if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) { +- local_bh_enable(); + local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); ++ local_bh_enable(); + return false; + } + *tun_ctx = *this_cpu_tun_ctx; +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch b/queue-7.0/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch new file mode 100644 index 0000000000..e7613b99c7 --- /dev/null +++ b/queue-7.0/netfilter-x_tables-add-and-use-xt_unregister_table_p.patch @@ -0,0 +1,349 @@ +From e0400b15717a478dbee83d7dd6cca013b1b71ee4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:15 +0200 +Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit + +From: Florian Westphal + +[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] + +Remove the copypasted variants of _pre_exit and add one single +function in the xtables core. ebtables is not compatible with +x_tables and therefore unchanged. + +This is a preparation patch to reduce noise in the followup +bug fixes. + +Reviewed-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + include/linux/netfilter_arp/arp_tables.h | 1 - + include/linux/netfilter_ipv4/ip_tables.h | 1 - + include/linux/netfilter_ipv6/ip6_tables.h | 1 - + net/ipv4/netfilter/arp_tables.c | 9 ------- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/ip_tables.c | 9 ------- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_nat.c | 1 + + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6_tables.c | 9 ------- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_nat.c | 1 + + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ + 19 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index b1235098db87c..196b6d03d08a6 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -301,6 +301,7 @@ struct xt_table *xt_register_table(struct net *net, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h +index a40aaf645fa47..05631a25e6229 100644 +--- a/include/linux/netfilter_arp/arp_tables.h ++++ b/include/linux/netfilter_arp/arp_tables.h +@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, + const struct arpt_replace *repl, + const struct nf_hook_ops *ops); + void arpt_unregister_table(struct net *net, const char *name); +-void arpt_unregister_table_pre_exit(struct net *net, const char *name); + extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); + +diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h +index 132b0e4a6d4df..13593391d6058 100644 +--- a/include/linux/netfilter_ipv4/ip_tables.h ++++ b/include/linux/netfilter_ipv4/ip_tables.h +@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *ops); + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name); + void ipt_unregister_table_exit(struct net *net, const char *name); + + /* Standard entry. */ +diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h +index 8b8885a73c764..c6d5b927830dd 100644 +--- a/include/linux/netfilter_ipv6/ip6_tables.h ++++ b/include/linux/netfilter_ipv6/ip6_tables.h +@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); + int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *ops); +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name); + void ip6t_unregister_table_exit(struct net *net, const char *name); + extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index c02e46a0271a0..bd348b7bad2c5 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1554,15 +1554,6 @@ int arpt_register_table(struct net *net, + return ret; + } + +-void arpt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +-EXPORT_SYMBOL(arpt_unregister_table_pre_exit); +- + void arpt_unregister_table(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 359d00d74095b..382345567a600 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) + + static void __net_exit arptable_filter_net_pre_exit(struct net *net) + { +- arpt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); + } + + static void __net_exit arptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 488c5945ebb23..864489928fb5a 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1756,14 +1756,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ipt_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ipt_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); +@@ -1854,7 +1846,6 @@ static void __exit ip_tables_fini(void) + } + + EXPORT_SYMBOL(ipt_register_table); +-EXPORT_SYMBOL(ipt_unregister_table_pre_exit); + EXPORT_SYMBOL(ipt_unregister_table_exit); + EXPORT_SYMBOL(ipt_do_table); + module_init(ip_tables_init); +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 595bfb492b1c1..0dea754a91209 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) + + static void __net_exit iptable_filter_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); + } + + static void __net_exit iptable_filter_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index db90db7057cc4..4d3b124923080 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) + + static void __net_exit iptable_mangle_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); + } + + static void __net_exit iptable_mangle_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 625a1ca13b1ba..8fc4912e790d8 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) + static void __net_exit iptable_nat_net_pre_exit(struct net *net) + { + ipt_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + } + + static void __net_exit iptable_nat_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index b46a790917306..6f7afec7954bd 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) + + static void __net_exit iptable_raw_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); + } + + static void __net_exit iptable_raw_net_exit(struct net *net) +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 2b89adc1e5751..81175c20ccbe8 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) + + static void __net_exit iptable_security_net_pre_exit(struct net *net) + { +- ipt_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); + } + + static void __net_exit iptable_security_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index dbe7c7acd702e..edf50bc7787e5 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1765,14 +1765,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +-void ip6t_unregister_table_pre_exit(struct net *net, const char *name) +-{ +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +- +- if (table) +- nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); +-} +- + void ip6t_unregister_table_exit(struct net *net, const char *name) + { + struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); +@@ -1864,7 +1856,6 @@ static void __exit ip6_tables_fini(void) + } + + EXPORT_SYMBOL(ip6t_register_table); +-EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); + EXPORT_SYMBOL(ip6t_unregister_table_exit); + EXPORT_SYMBOL(ip6t_do_table); + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index 9dcd4501fe800..cf561919bde84 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) + + static void __net_exit ip6table_filter_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "filter"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); + } + + static void __net_exit ip6table_filter_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index ce2cbce9e3ed3..1a758f2bc5379 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) + + static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "mangle"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); + } + + static void __net_exit ip6table_mangle_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index 5be723232df8f..bb8aa3fc42b45 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) + static void __net_exit ip6table_nat_net_pre_exit(struct net *net) + { + ip6t_nat_unregister_lookups(net); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + } + + static void __net_exit ip6table_nat_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 8af0f8bd036dc..923455921c1dd 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) + + static void __net_exit ip6table_raw_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "raw"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); + } + + static void __net_exit ip6table_raw_net_exit(struct net *net) +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 66018b169b010..c44834d93fc79 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) + + static void __net_exit ip6table_security_net_pre_exit(struct net *net) + { +- ip6t_unregister_table_pre_exit(net, "security"); ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); + } + + static void __net_exit ip6table_security_net_exit(struct net *net) +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index e5fda8c2fc6cb..92fb3a64f70d9 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1583,6 +1583,35 @@ void *xt_unregister_table(struct xt_table *table) + return private; + } + EXPORT_SYMBOL_GPL(xt_unregister_table); ++ ++/** ++ * xt_unregister_table_pre_exit - pre-shutdown unregister of a table ++ * @net: network namespace ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Unregisters the specified netfilter table from the given network namespace ++ * and also unregisters the hooks from netfilter core: no new packets will be ++ * processed. ++ */ ++void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(t, &xt_net->tables[af], list) { ++ if (strcmp(t->name, name) == 0) { ++ mutex_unlock(&xt[af].mutex); ++ ++ if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ ++ nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); ++ return; ++ } ++ } ++ mutex_unlock(&xt[af].mutex); ++} ++EXPORT_SYMBOL(xt_unregister_table_pre_exit); + #endif + + #ifdef CONFIG_PROC_FS +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch b/queue-7.0/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch new file mode 100644 index 0000000000..5bae59c4d3 --- /dev/null +++ b/queue-7.0/netfilter-x_tables-add-and-use-xtables_unregister_ta.patch @@ -0,0 +1,334 @@ +From 60c951b1feab6188cfc82dfb73caa2a1a0223587 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:17 +0200 +Subject: netfilter: x_tables: add and use xtables_unregister_table_exit + +From: Florian Westphal + +[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] + +Previous change added xtables_unregister_table_pre_exit to detach the +table from the packetpath and to unlink it from the active table list. +In case of rmmod, userspace that is doing set/getsockopt for this table +will not be able to re-instantiate the table: + 1. The larval table has been removed already + 2. existing instantiated table is no longer on the xt pernet table list. + +This adds the second stage helper: + +unlink the table from the dying list, free the hook ops (if any) and do +the audit notification. It replaces xt_unregister_table(). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 2 +- + net/ipv4/netfilter/arp_tables.c | 9 ++-- + net/ipv4/netfilter/ip_tables.c | 9 ++-- + net/ipv4/netfilter/iptable_nat.c | 5 +- + net/ipv6/netfilter/ip6_tables.c | 9 ++-- + net/ipv6/netfilter/ip6table_nat.c | 5 +- + net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- + 7 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 196b6d03d08a6..6fd365f7b35b0 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -300,8 +300,8 @@ struct xt_table *xt_register_table(struct net *net, + const struct nf_hook_ops *template_ops, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +-void *xt_unregister_table(struct xt_table *table); + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); + + struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index bd348b7bad2c5..ad2259678c785 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len + + static void __arpt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; ++ void *loc_cpu_entry; + struct arpt_entry *iter; + +- private = xt_unregister_table(table); +- + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; + xt_entry_foreach(iter, loc_cpu_entry, private->size) +@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int arpt_register_table(struct net *net, +@@ -1556,7 +1555,7 @@ int arpt_register_table(struct net *net, + + void arpt_unregister_table(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); + + if (table) + __arpt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 864489928fb5a..5cbdb0815857f 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ipt_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ipt_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ipt_register_table(struct net *net, const struct xt_table *table, +@@ -1758,7 +1757,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + + void ipt_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); + + if (table) + __ipt_unregister_table(net, table); +diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c +index 8fc4912e790d8..a0df725540251 100644 +--- a/net/ipv4/netfilter/iptable_nat.c ++++ b/net/ipv4/netfilter/iptable_nat.c +@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) + } + + ret = ipt_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); ++ synchronize_rcu(); + ipt_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index edf50bc7787e5..9d9c3763f2f5e 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) + + static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + { +- struct xt_table_info *private; +- void *loc_cpu_entry; ++ struct xt_table_info *private = table->private; + struct module *table_owner = table->me; + struct ip6t_entry *iter; +- +- private = xt_unregister_table(table); ++ void *loc_cpu_entry; + + /* Decrease module usage counts and free resources */ + loc_cpu_entry = private->entries; +@@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) + if (private->number > private->initial_entries) + module_put(table_owner); + xt_free_table_info(private); ++ kfree(table); + } + + int ip6t_register_table(struct net *net, const struct xt_table *table, +@@ -1767,7 +1766,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + + void ip6t_unregister_table_exit(struct net *net, const char *name) + { +- struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); ++ struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); + + if (table) + __ip6t_unregister_table(net, table); +diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c +index bb8aa3fc42b45..c2394e2c94b56 100644 +--- a/net/ipv6/netfilter/ip6table_nat.c ++++ b/net/ipv6/netfilter/ip6table_nat.c +@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) + } + + ret = ip6t_nat_register_lookups(net); +- if (ret < 0) ++ if (ret < 0) { ++ xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); ++ synchronize_rcu(); + ip6t_unregister_table_exit(net, "nat"); ++ } + + kfree(repl); + return ret; +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 92fb3a64f70d9..8050cc06a9a30 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; + + struct xt_pernet { + struct list_head tables[NFPROTO_NUMPROTO]; ++ ++ /* stash area used during netns exit */ ++ struct list_head dead_tables[NFPROTO_NUMPROTO]; + }; + + struct compat_delta { +@@ -1567,23 +1570,6 @@ struct xt_table *xt_register_table(struct net *net, + } + EXPORT_SYMBOL_GPL(xt_register_table); + +-void *xt_unregister_table(struct xt_table *table) +-{ +- struct xt_table_info *private; +- +- mutex_lock(&xt[table->af].mutex); +- private = table->private; +- list_del(&table->list); +- mutex_unlock(&xt[table->af].mutex); +- audit_log_nfcfg(table->name, table->af, private->number, +- AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); +- kfree(table->ops); +- kfree(table); +- +- return private; +-} +-EXPORT_SYMBOL_GPL(xt_unregister_table); +- + /** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace +@@ -1593,6 +1579,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. ++ * ++ * This must be called prior to xt_unregister_table_exit() from the pernet ++ * .pre_exit callback. After this call, the table is no longer visible to ++ * the get/setsockopt path. In case of rmmod, module exit path must have ++ * called xt_unregister_template() prior to unregistering pernet ops to ++ * prevent re-instantiation of the table. ++ * ++ * See also: xt_unregister_table_exit() + */ + void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + { +@@ -1602,6 +1596,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { ++ list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ +@@ -1612,6 +1607,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) + mutex_unlock(&xt[af].mutex); + } + EXPORT_SYMBOL(xt_unregister_table_pre_exit); ++ ++/** ++ * xt_unregister_table_exit - remove a table during namespace teardown ++ * @net: the network namespace from which to unregister the table ++ * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) ++ * @name: name of the table to unregister ++ * ++ * Completes the unregister process for a table. This must be called from ++ * the pernet ops .exit callback. This is the second stage after ++ * xt_unregister_table_pre_exit(). ++ * ++ * pair with xt_unregister_table_pre_exit() during namespace shutdown. ++ * ++ * Return: the unregistered table or NULL if the table was never ++ * instantiated. The caller needs to kfree() the table after it ++ * has removed the family specific matches/targets. ++ */ ++struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) ++{ ++ struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *table; ++ ++ mutex_lock(&xt[af].mutex); ++ list_for_each_entry(table, &xt_net->dead_tables[af], list) { ++ struct nf_hook_ops *ops = NULL; ++ ++ if (strcmp(table->name, name) != 0) ++ continue; ++ ++ list_del(&table->list); ++ ++ audit_log_nfcfg(table->name, table->af, table->private->number, ++ AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); ++ swap(table->ops, ops); ++ mutex_unlock(&xt[af].mutex); ++ ++ kfree(ops); ++ return table; ++ } ++ mutex_unlock(&xt[af].mutex); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_unregister_table_exit); + #endif + + #ifdef CONFIG_PROC_FS +@@ -2058,8 +2097,10 @@ static int __net_init xt_net_init(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + INIT_LIST_HEAD(&xt_net->tables[i]); ++ INIT_LIST_HEAD(&xt_net->dead_tables[i]); ++ } + return 0; + } + +@@ -2068,8 +2109,10 @@ static void __net_exit xt_net_exit(struct net *net) + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + int i; + +- for (i = 0; i < NFPROTO_NUMPROTO; i++) ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { + WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); ++ WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); ++ } + } + + static struct pernet_operations xt_net_ops = { +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-allocate-hook-ops-while-under-mut.patch b/queue-7.0/netfilter-x_tables-allocate-hook-ops-while-under-mut.patch new file mode 100644 index 0000000000..dd6ea76844 --- /dev/null +++ b/queue-7.0/netfilter-x_tables-allocate-hook-ops-while-under-mut.patch @@ -0,0 +1,362 @@ +From 536643e3a80c57f653b90609ddaee19593d323ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:14 +0200 +Subject: netfilter: x_tables: allocate hook ops while under mutex + +From: Florian Westphal + +[ Upstream commit b62eb8dcf2c47d4d676a434efbd57c4f776f7829 ] + +arp/ip(6)t_register_table() add the table to the per-netns list via +xt_register_table() before allocating the per-netns hook ops copy +via kmemdup_array(). This leaves a window where the table is +visible in the list with ops=NULL. + +If the pernet exit happens runs concurrently the pre_exit callback finds +the table via xt_find_table() and passes the NULL ops pointer to +nf_unregister_net_hooks(), causing a NULL dereference: + + general protection fault in nf_unregister_net_hooks+0xbc/0x150 + RIP: nf_unregister_net_hooks (net/netfilter/core.c:613) + Call Trace: + ipt_unregister_table_pre_exit + iptable_mangle_net_pre_exit + ops_pre_exit_list + cleanup_net + +Fix by moving the ops allocation into the xtables core so the table is +never in the list without valid ops. Also ensure the table is no longer +processing packets before its torn down on error unwind. +nf_register_net_hooks might have published at least one hook; call +synchronize_rcu() if there was an error. + +audit log register message gets deferred until all operations have +passed, this avoids need to emit another ureg message in case of +error unwinding. + +Based on earlier patch by Tristan Madani. + +Fixes: f9006acc8dfe5 ("netfilter: arp_tables: pass table pointer via nf_hook_ops") +Fixes: ee177a54413a ("netfilter: ip6_tables: pass table pointer via nf_hook_ops") +Fixes: ae689334225f ("netfilter: ip_tables: pass table pointer via nf_hook_ops") +Link: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Tristan Madani +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/x_tables.h | 1 + + net/ipv4/netfilter/arp_tables.c | 35 +++------------------ + net/ipv4/netfilter/ip_tables.c | 41 +++--------------------- + net/ipv6/netfilter/ip6_tables.c | 38 +++-------------------- + net/netfilter/x_tables.c | 50 +++++++++++++++++++++++++----- + 5 files changed, 55 insertions(+), 110 deletions(-) + +diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h +index 77c778d84d4cb..b1235098db87c 100644 +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -297,6 +297,7 @@ struct xt_counters *xt_counters_alloc(unsigned int counters); + + struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, ++ const struct nf_hook_ops *template_ops, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); + void *xt_unregister_table(struct xt_table *table); +diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c +index 97ead883e4a13..c02e46a0271a0 100644 +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1522,13 +1522,11 @@ int arpt_register_table(struct net *net, + const struct arpt_replace *repl, + const struct nf_hook_ops *template_ops) + { +- struct nf_hook_ops *ops; +- unsigned int num_ops; +- int ret, i; +- struct xt_table_info *newinfo; + struct xt_table_info bootstrap = {0}; +- void *loc_cpu_entry; ++ struct xt_table_info *newinfo; + struct xt_table *new_table; ++ void *loc_cpu_entry; ++ int ret; + + newinfo = xt_alloc_table_info(repl->size); + if (!newinfo) +@@ -1543,7 +1541,7 @@ int arpt_register_table(struct net *net, + return ret; + } + +- new_table = xt_register_table(net, table, &bootstrap, newinfo); ++ new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); + if (IS_ERR(new_table)) { + struct arpt_entry *iter; + +@@ -1553,31 +1551,6 @@ int arpt_register_table(struct net *net, + return PTR_ERR(new_table); + } + +- num_ops = hweight32(table->valid_hooks); +- if (num_ops == 0) { +- ret = -EINVAL; +- goto out_free; +- } +- +- ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); +- if (!ops) { +- ret = -ENOMEM; +- goto out_free; +- } +- +- for (i = 0; i < num_ops; i++) +- ops[i].priv = new_table; +- +- new_table->ops = ops; +- +- ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret != 0) +- goto out_free; +- +- return ret; +- +-out_free: +- __arpt_unregister_table(net, new_table); + return ret; + } + +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 23c8deff8095a..488c5945ebb23 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1724,13 +1724,11 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + const struct ipt_replace *repl, + const struct nf_hook_ops *template_ops) + { +- struct nf_hook_ops *ops; +- unsigned int num_ops; +- int ret, i; +- struct xt_table_info *newinfo; + struct xt_table_info bootstrap = {0}; +- void *loc_cpu_entry; ++ struct xt_table_info *newinfo; + struct xt_table *new_table; ++ void *loc_cpu_entry; ++ int ret; + + newinfo = xt_alloc_table_info(repl->size); + if (!newinfo) +@@ -1745,7 +1743,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +- new_table = xt_register_table(net, table, &bootstrap, newinfo); ++ new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); + if (IS_ERR(new_table)) { + struct ipt_entry *iter; + +@@ -1755,37 +1753,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, + return PTR_ERR(new_table); + } + +- /* No template? No need to do anything. This is used by 'nat' table, it registers +- * with the nat core instead of the netfilter core. +- */ +- if (!template_ops) +- return 0; +- +- num_ops = hweight32(table->valid_hooks); +- if (num_ops == 0) { +- ret = -EINVAL; +- goto out_free; +- } +- +- ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); +- if (!ops) { +- ret = -ENOMEM; +- goto out_free; +- } +- +- for (i = 0; i < num_ops; i++) +- ops[i].priv = new_table; +- +- new_table->ops = ops; +- +- ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret != 0) +- goto out_free; +- +- return ret; +- +-out_free: +- __ipt_unregister_table(net, new_table); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c +index d585ac3c11133..dbe7c7acd702e 100644 +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1733,13 +1733,11 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + const struct ip6t_replace *repl, + const struct nf_hook_ops *template_ops) + { +- struct nf_hook_ops *ops; +- unsigned int num_ops; +- int ret, i; +- struct xt_table_info *newinfo; + struct xt_table_info bootstrap = {0}; +- void *loc_cpu_entry; ++ struct xt_table_info *newinfo; + struct xt_table *new_table; ++ void *loc_cpu_entry; ++ int ret; + + newinfo = xt_alloc_table_info(repl->size); + if (!newinfo) +@@ -1754,7 +1752,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return ret; + } + +- new_table = xt_register_table(net, table, &bootstrap, newinfo); ++ new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); + if (IS_ERR(new_table)) { + struct ip6t_entry *iter; + +@@ -1764,34 +1762,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, + return PTR_ERR(new_table); + } + +- if (!template_ops) +- return 0; +- +- num_ops = hweight32(table->valid_hooks); +- if (num_ops == 0) { +- ret = -EINVAL; +- goto out_free; +- } +- +- ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); +- if (!ops) { +- ret = -ENOMEM; +- goto out_free; +- } +- +- for (i = 0; i < num_ops; i++) +- ops[i].priv = new_table; +- +- new_table->ops = ops; +- +- ret = nf_register_net_hooks(net, ops, num_ops); +- if (ret != 0) +- goto out_free; +- +- return ret; +- +-out_free: +- __ip6t_unregister_table(net, new_table); + return ret; + } + +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index f694eb72e48db..e5fda8c2fc6cb 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1475,7 +1475,6 @@ xt_replace_table(struct xt_table *table, unsigned int num_counters, + private = do_replace_table(table, num_counters, newinfo, error); + if (private) + audit_log_nfcfg(table->name, table->af, private->number, +- !private->number ? AUDIT_XT_OP_REGISTER : + AUDIT_XT_OP_REPLACE, + GFP_KERNEL); + +@@ -1485,20 +1484,32 @@ EXPORT_SYMBOL_GPL(xt_replace_table); + + struct xt_table *xt_register_table(struct net *net, + const struct xt_table *input_table, ++ const struct nf_hook_ops *template_ops, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo) + { + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); ++ struct xt_table *t, *table = NULL; ++ struct nf_hook_ops *ops = NULL; + struct xt_table_info *private; +- struct xt_table *t, *table; +- int ret; ++ unsigned int num_ops; ++ int ret = -EINVAL; ++ ++ num_ops = hweight32(input_table->valid_hooks); ++ if (num_ops == 0) ++ goto out; ++ ++ ret = -ENOMEM; ++ if (template_ops) { ++ ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); ++ if (!ops) ++ goto out; ++ } + + /* Don't add one object to multiple lists. */ + table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL); +- if (!table) { +- ret = -ENOMEM; ++ if (!table) + goto out; +- } + + mutex_lock(&xt[table->af].mutex); + /* Don't autoload: we'd eat our tail... */ +@@ -1512,7 +1523,7 @@ struct xt_table *xt_register_table(struct net *net, + /* Simplifies replace_table code. */ + table->private = bootstrap; + +- if (!xt_replace_table(table, 0, newinfo, &ret)) ++ if (!do_replace_table(table, 0, newinfo, &ret)) + goto unlock; + + private = table->private; +@@ -1521,14 +1532,37 @@ struct xt_table *xt_register_table(struct net *net, + /* save number of initial entries */ + private->initial_entries = private->number; + ++ if (ops) { ++ int i; ++ ++ for (i = 0; i < num_ops; i++) ++ ops[i].priv = table; ++ ++ ret = nf_register_net_hooks(net, ops, num_ops); ++ if (ret != 0) { ++ mutex_unlock(&xt[table->af].mutex); ++ /* nf_register_net_hooks() might have published a ++ * base chain before internal error unwind. ++ */ ++ synchronize_rcu(); ++ goto out; ++ } ++ ++ table->ops = ops; ++ } ++ ++ audit_log_nfcfg(table->name, table->af, private->number, ++ AUDIT_XT_OP_REGISTER, GFP_KERNEL); ++ + list_add(&table->list, &xt_net->tables[table->af]); + mutex_unlock(&xt[table->af].mutex); + return table; + + unlock: + mutex_unlock(&xt[table->af].mutex); +- kfree(table); + out: ++ kfree(table); ++ kfree(ops); + return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(xt_register_table); +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-allow-initial-table-replace-witho.patch b/queue-7.0/netfilter-x_tables-allow-initial-table-replace-witho.patch new file mode 100644 index 0000000000..26a306a9a6 --- /dev/null +++ b/queue-7.0/netfilter-x_tables-allow-initial-table-replace-witho.patch @@ -0,0 +1,75 @@ +From e041810f708cabcdf34162d0493635c94d16dd37 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:13 +0200 +Subject: netfilter: x_tables: allow initial table replace without emitting + audit log message + +From: Florian Westphal + +[ Upstream commit 8e72510db9fa2d41f2b06d5c01fe9020e076fee4 ] + +At the moment we emit the audit log a bit too early, which makes it +necessary to also emit an unregister log in case we have to unwind +errors after possible hook register failure. + +Followup patch will be slightly simpler if we can delay the +register message until after the hooks have been wired up. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Stable-dep-of: b62eb8dcf2c4 ("netfilter: x_tables: allocate hook ops while under mutex") +Signed-off-by: Sasha Levin +--- + net/netfilter/x_tables.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index b39017c805484..f694eb72e48db 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -1405,11 +1405,9 @@ struct xt_counters *xt_counters_alloc(unsigned int counters) + } + EXPORT_SYMBOL(xt_counters_alloc); + +-struct xt_table_info * +-xt_replace_table(struct xt_table *table, +- unsigned int num_counters, +- struct xt_table_info *newinfo, +- int *error) ++static struct xt_table_info * ++do_replace_table(struct xt_table *table, unsigned int num_counters, ++ struct xt_table_info *newinfo, int *error) + { + struct xt_table_info *private; + unsigned int cpu; +@@ -1464,10 +1462,23 @@ xt_replace_table(struct xt_table *table, + } + } + +- audit_log_nfcfg(table->name, table->af, private->number, +- !private->number ? AUDIT_XT_OP_REGISTER : +- AUDIT_XT_OP_REPLACE, +- GFP_KERNEL); ++ return private; ++} ++ ++struct xt_table_info * ++xt_replace_table(struct xt_table *table, unsigned int num_counters, ++ struct xt_table_info *newinfo, ++ int *error) ++{ ++ struct xt_table_info *private; ++ ++ private = do_replace_table(table, num_counters, newinfo, error); ++ if (private) ++ audit_log_nfcfg(table->name, table->af, private->number, ++ !private->number ? AUDIT_XT_OP_REGISTER : ++ AUDIT_XT_OP_REPLACE, ++ GFP_KERNEL); ++ + return private; + } + EXPORT_SYMBOL_GPL(xt_replace_table); +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-close-dangling-table-module-init-.patch b/queue-7.0/netfilter-x_tables-close-dangling-table-module-init-.patch new file mode 100644 index 0000000000..96b3e0012d --- /dev/null +++ b/queue-7.0/netfilter-x_tables-close-dangling-table-module-init-.patch @@ -0,0 +1,406 @@ +From 75ab2c1397839b84fe9218a85af1ab749179eedf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:20 +0200 +Subject: netfilter: x_tables: close dangling table module init race + +From: Florian Westphal + +[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] + +Similar to the previous ebtables patch: +template add exposes the table to userspace, we must do this last to +rnsure the pernet ops are set up (contain the destructors). + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- + net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ + net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- + net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- + net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- + net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- + net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- + 9 files changed, 105 insertions(+), 99 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 382345567a600..370b635e3523b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { + + static int __init arptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- arptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); +- if (IS_ERR(arpfilter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(arpfilter_ops)) + return PTR_ERR(arpfilter_ops); +- } + + ret = register_pernet_subsys(&arptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ arptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(arpfilter_ops); +- return ret; ++ unregister_pernet_subsys(&arptable_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(arpfilter_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 0dea754a91209..672d7da1071d3 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { + + static int __init iptable_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- iptable_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&iptable_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ++ iptable_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_filter_net_ops); ++ goto err_free; + } + + return 0; ++err_free: ++ kfree(filter_ops); ++ return ret; + } + + static void __exit iptable_filter_fini(void) +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 4d3b124923080..13d25d9a4610e 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -111,25 +111,26 @@ static struct pernet_operations iptable_mangle_net_ops = { + + static int __init iptable_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- iptable_mangle_table_init); +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); +- ret = PTR_ERR(mangle_ops); +- return ret; +- } ++ if (IS_ERR(mangle_ops)) ++ return PTR_ERR(mangle_ops); + + ret = register_pernet_subsys(&iptable_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ iptable_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 6f7afec7954bd..2745c22f4034d 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, +- iptable_raw_table_init); +- if (ret < 0) +- return ret; +- + rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&iptable_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ++ iptable_raw_table_init); + if (ret < 0) { +- xt_unregister_template(table); +- kfree(rawtable_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index 81175c20ccbe8..491894511c544 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { + + static int __init iptable_security_init(void) + { +- int ret = xt_register_template(&security_table, +- iptable_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&iptable_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ iptable_security_table_init); + if (ret < 0) { +- xt_unregister_template(&security_table); +- kfree(sectbl_ops); +- return ret; ++ unregister_pernet_subsys(&iptable_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index cf561919bde84..b074fc4776764 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { + + static int __init ip6table_filter_init(void) + { +- int ret = xt_register_template(&packet_filter, +- ip6table_filter_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); +- if (IS_ERR(filter_ops)) { +- xt_unregister_template(&packet_filter); ++ if (IS_ERR(filter_ops)) + return PTR_ERR(filter_ops); +- } + + ret = register_pernet_subsys(&ip6table_filter_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_filter, ip6table_filter_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_filter); +- kfree(filter_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_filter_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(filter_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 1a758f2bc5379..e6ee036a9b2c5 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -104,25 +104,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { + + static int __init ip6table_mangle_init(void) + { +- int ret = xt_register_template(&packet_mangler, +- ip6table_mangle_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); +- if (IS_ERR(mangle_ops)) { +- xt_unregister_template(&packet_mangler); ++ if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); +- } + + ret = register_pernet_subsys(&ip6table_mangle_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&packet_mangler, ++ ip6table_mangle_table_init); + if (ret < 0) { +- xt_unregister_template(&packet_mangler); +- kfree(mangle_ops); +- return ret; ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(mangle_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index 923455921c1dd..3b161ee875bcc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) + pr_info("Enabling raw table before defrag\n"); + } + +- ret = xt_register_template(table, ip6table_raw_table_init); +- if (ret < 0) +- return ret; +- + /* Register hooks */ + rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); +- if (IS_ERR(rawtable_ops)) { +- xt_unregister_template(table); ++ if (IS_ERR(rawtable_ops)) + return PTR_ERR(rawtable_ops); +- } + + ret = register_pernet_subsys(&ip6table_raw_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(table, ip6table_raw_table_init); + if (ret < 0) { +- kfree(rawtable_ops); +- xt_unregister_template(table); +- return ret; ++ unregister_pernet_subsys(&ip6table_raw_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(rawtable_ops); + return ret; + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index c44834d93fc79..4bd5d97b8ab65 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { + + static int __init ip6table_security_init(void) + { +- int ret = xt_register_template(&security_table, +- ip6table_security_table_init); +- +- if (ret < 0) +- return ret; ++ int ret; + + sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); +- if (IS_ERR(sectbl_ops)) { +- xt_unregister_template(&security_table); ++ if (IS_ERR(sectbl_ops)) + return PTR_ERR(sectbl_ops); +- } + + ret = register_pernet_subsys(&ip6table_security_net_ops); ++ if (ret < 0) ++ goto err_free; ++ ++ ret = xt_register_template(&security_table, ++ ip6table_security_table_init); + if (ret < 0) { +- kfree(sectbl_ops); +- xt_unregister_template(&security_table); +- return ret; ++ unregister_pernet_subsys(&ip6table_security_net_ops); ++ goto err_free; + } + ++ return 0; ++err_free: ++ kfree(sectbl_ops); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/netfilter-x_tables-unregister-the-templates-first.patch b/queue-7.0/netfilter-x_tables-unregister-the-templates-first.patch new file mode 100644 index 0000000000..a837a0992b --- /dev/null +++ b/queue-7.0/netfilter-x_tables-unregister-the-templates-first.patch @@ -0,0 +1,164 @@ +From 2f1ee523a7603dce736fb467b6a2e99c3dff3215 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 12:07:16 +0200 +Subject: netfilter: x_tables: unregister the templates first + +From: Florian Westphal + +[ Upstream commit d338693d778579b676a61346849bebd892427158 ] + +When the module is going away we need to zap the template +first. Else there is a small race window where userspace +could instantiate a new table after the pernet exit function +has removed the current table. + +Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") +Reported-by: Tristan Madani +Reviewed-by: Tristan Madani +Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/ipv4/netfilter/arptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_filter.c | 2 +- + net/ipv4/netfilter/iptable_mangle.c | 2 +- + net/ipv4/netfilter/iptable_raw.c | 2 +- + net/ipv4/netfilter/iptable_security.c | 2 +- + net/ipv6/netfilter/ip6table_filter.c | 2 +- + net/ipv6/netfilter/ip6table_mangle.c | 2 +- + net/ipv6/netfilter/ip6table_raw.c | 2 +- + net/ipv6/netfilter/ip6table_security.c | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c +index 78cd5ee24448f..359d00d74095b 100644 +--- a/net/ipv4/netfilter/arptable_filter.c ++++ b/net/ipv4/netfilter/arptable_filter.c +@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) + + static void __exit arptable_filter_fini(void) + { +- unregister_pernet_subsys(&arptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&arptable_filter_net_ops); + kfree(arpfilter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c +index 3ab908b747951..595bfb492b1c1 100644 +--- a/net/ipv4/netfilter/iptable_filter.c ++++ b/net/ipv4/netfilter/iptable_filter.c +@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) + + static void __exit iptable_filter_fini(void) + { +- unregister_pernet_subsys(&iptable_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&iptable_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c +index 385d945d8ebea..db90db7057cc4 100644 +--- a/net/ipv4/netfilter/iptable_mangle.c ++++ b/net/ipv4/netfilter/iptable_mangle.c +@@ -135,8 +135,8 @@ static int __init iptable_mangle_init(void) + + static void __exit iptable_mangle_fini(void) + { +- unregister_pernet_subsys(&iptable_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&iptable_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c +index 0e7f53964d0af..b46a790917306 100644 +--- a/net/ipv4/netfilter/iptable_raw.c ++++ b/net/ipv4/netfilter/iptable_raw.c +@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) + + static void __exit iptable_raw_fini(void) + { ++ xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&iptable_raw_net_ops); + kfree(rawtable_ops); +- xt_unregister_template(&packet_raw); + } + + module_init(iptable_raw_init); +diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c +index d885443cb2679..2b89adc1e5751 100644 +--- a/net/ipv4/netfilter/iptable_security.c ++++ b/net/ipv4/netfilter/iptable_security.c +@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) + + static void __exit iptable_security_fini(void) + { ++ xt_unregister_template(&security_table); + unregister_pernet_subsys(&iptable_security_net_ops); + kfree(sectbl_ops); +- xt_unregister_template(&security_table); + } + + module_init(iptable_security_init); +diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c +index e8992693e14a0..9dcd4501fe800 100644 +--- a/net/ipv6/netfilter/ip6table_filter.c ++++ b/net/ipv6/netfilter/ip6table_filter.c +@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) + + static void __exit ip6table_filter_fini(void) + { +- unregister_pernet_subsys(&ip6table_filter_net_ops); + xt_unregister_template(&packet_filter); ++ unregister_pernet_subsys(&ip6table_filter_net_ops); + kfree(filter_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c +index 8dd4cd0c47bd4..ce2cbce9e3ed3 100644 +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -128,8 +128,8 @@ static int __init ip6table_mangle_init(void) + + static void __exit ip6table_mangle_fini(void) + { +- unregister_pernet_subsys(&ip6table_mangle_net_ops); + xt_unregister_template(&packet_mangler); ++ unregister_pernet_subsys(&ip6table_mangle_net_ops); + kfree(mangle_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c +index fc9f6754028f2..8af0f8bd036dc 100644 +--- a/net/ipv6/netfilter/ip6table_raw.c ++++ b/net/ipv6/netfilter/ip6table_raw.c +@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) + + static void __exit ip6table_raw_fini(void) + { +- unregister_pernet_subsys(&ip6table_raw_net_ops); + xt_unregister_template(&packet_raw); ++ unregister_pernet_subsys(&ip6table_raw_net_ops); + kfree(rawtable_ops); + } + +diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c +index 4df14a9bae782..66018b169b010 100644 +--- a/net/ipv6/netfilter/ip6table_security.c ++++ b/net/ipv6/netfilter/ip6table_security.c +@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) + + static void __exit ip6table_security_fini(void) + { +- unregister_pernet_subsys(&ip6table_security_net_ops); + xt_unregister_template(&security_table); ++ unregister_pernet_subsys(&ip6table_security_net_ops); + kfree(sectbl_ops); + } + +-- +2.53.0 + diff --git a/queue-7.0/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch b/queue-7.0/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch new file mode 100644 index 0000000000..7c90f77cd8 --- /dev/null +++ b/queue-7.0/netfs-afs-fix-write-skipping-in-dir-link-writepages.patch @@ -0,0 +1,90 @@ +From ee9a511c938f8f358feae301320e948f774d6cab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:34:00 +0100 +Subject: netfs, afs: Fix write skipping in dir/link writepages + +From: David Howells + +[ Upstream commit 9871938f99cc6cb266a77265491660e2375271f5 ] + +Fix netfs_write_single() and afs_single_writepages() to better handle a +write that would be skipped due to lock contention and WB_SYNC_NONE by +returning 1 from netfs_write_single() if it skipped and making +afs_single_writepages() skip also. If a skip occurs, the inode must be +re-marked as the VFS may have cleared the mark. + +This is really only theoretical for directories in netfs_write_single() as +the only path to that is through afs_single_writepages() that takes the +->validate_lock around it, thereby serialising it. + +Fixes: 6dd80936618c ("afs: Use netfslib for directories") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-24-dhowells@redhat.com +cc: Marc Dionne +cc: linux-afs@lists.infradead.org +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/afs/dir.c | 11 ++++++++++- + fs/netfs/write_issue.c | 7 ++++++- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/fs/afs/dir.c b/fs/afs/dir.c +index 78caef3f13388..068a892d39c4e 100644 +--- a/fs/afs/dir.c ++++ b/fs/afs/dir.c +@@ -2206,7 +2206,14 @@ int afs_single_writepages(struct address_space *mapping, + /* Need to lock to prevent the folio queue and folios from being thrown + * away. + */ +- down_read(&dvnode->validate_lock); ++ if (!down_read_trylock(&dvnode->validate_lock)) { ++ if (wbc->sync_mode == WB_SYNC_NONE) { ++ /* The VFS will have undirtied the inode. */ ++ netfs_single_mark_inode_dirty(&dvnode->netfs.inode); ++ return 0; ++ } ++ down_read(&dvnode->validate_lock); ++ } + + if (is_dir ? + test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : +@@ -2214,6 +2221,8 @@ int afs_single_writepages(struct address_space *mapping, + iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, + i_size_read(&dvnode->netfs.inode)); + ret = netfs_writeback_single(mapping, wbc, &iter); ++ if (ret == 1) ++ ret = 0; /* Skipped write due to lock conflict. */ + } + + up_read(&dvnode->validate_lock); +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 03961622996be..c03c7cc45e471 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -830,6 +830,9 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + * + * Write a monolithic, non-pagecache object back to the server and/or + * the cache. ++ * ++ * Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_NONE; ++ * or a negative error code. + */ + int netfs_writeback_single(struct address_space *mapping, + struct writeback_control *wbc, +@@ -846,8 +849,10 @@ int netfs_writeback_single(struct address_space *mapping, + + if (!mutex_trylock(&ictx->wb_lock)) { + if (wbc->sync_mode == WB_SYNC_NONE) { ++ /* The VFS will have undirtied the inode. */ ++ netfs_single_mark_inode_dirty(&ictx->inode); + netfs_stat(&netfs_n_wb_lock_skip); +- return 0; ++ return 1; + } + netfs_stat(&netfs_n_wb_lock_wait); + mutex_lock(&ictx->wb_lock); +-- +2.53.0 + diff --git a/queue-7.0/netfs-defer-the-emission-of-trace_netfs_folio.patch b/queue-7.0/netfs-defer-the-emission-of-trace_netfs_folio.patch new file mode 100644 index 0000000000..23e3ed112c --- /dev/null +++ b/queue-7.0/netfs-defer-the-emission-of-trace_netfs_folio.patch @@ -0,0 +1,116 @@ +From b379adadad115172aa3f3147cd0e035c6834bb51 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:49 +0100 +Subject: netfs: Defer the emission of trace_netfs_folio() + +From: David Howells + +[ Upstream commit daeb443b92817021c1234e8eded219e164b7c35d ] + +Change netfs_perform_write() to keep the netfs_folio trace value in a +variable and emit it later to make it easier to choose the value displayed. +This is a prerequisite for a subsequent patch. + +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten") +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index c887a30c14d91..a695d5168b2fc 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -150,6 +150,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + do { ++ enum netfs_folio_trace trace; + struct netfs_folio *finfo; + struct netfs_group *group; + unsigned long long fpos; +@@ -223,7 +224,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_folio_is_uptodate); ++ trace = netfs_folio_is_uptodate; + goto copied; + } + +@@ -239,7 +240,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + folio_zero_segment(folio, offset + copied, flen); + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_modify_and_clear); ++ trace = netfs_modify_and_clear; + goto copied; + } + +@@ -257,7 +258,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_whole_folio_modify); ++ trace = netfs_whole_folio_modify; + goto copied; + } + +@@ -284,7 +285,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + netfs_set_group(folio, netfs_group); +- trace_netfs_folio(folio, netfs_just_prefetch); ++ trace = netfs_just_prefetch; + goto copied; + } + +@@ -298,7 +299,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (offset == 0 && copied == flen) { + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); +- trace_netfs_folio(folio, netfs_streaming_filled_page); ++ trace = netfs_streaming_filled_page; + goto copied; + } + +@@ -313,7 +314,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len = copied; + folio_attach_private(folio, (void *)((unsigned long)finfo | + NETFS_FOLIO_INFO)); +- trace_netfs_folio(folio, netfs_streaming_write); ++ trace = netfs_streaming_write; + goto copied; + } + +@@ -333,9 +334,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + folio_detach_private(folio); + folio_mark_uptodate(folio); + kfree(finfo); +- trace_netfs_folio(folio, netfs_streaming_cont_filled_page); ++ trace = netfs_streaming_cont_filled_page; + } else { +- trace_netfs_folio(folio, netfs_streaming_write_cont); ++ trace = netfs_streaming_write_cont; + } + goto copied; + } +@@ -351,6 +352,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + continue; + + copied: ++ trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); + + /* Update the inode size if we moved the EOF marker */ +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch b/queue-7.0/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch new file mode 100644 index 0000000000..263cbd1542 --- /dev/null +++ b/queue-7.0/netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch @@ -0,0 +1,342 @@ +From 630a2608b51d1857304074b5f75d933e1ae6cf55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:38 +0100 +Subject: netfs: Fix cancellation of a DIO and single read subrequests + +From: David Howells + +[ Upstream commit 6f0f7ac1915abc0d202f0eb4b003a6548a5ba60d ] + +When the preparation of a new subrequest for a read fails, if the +subrequest has already been added to the stream->subrequests list, it can't +simply be put and abandoned as the collector may see it. Also, if it +hasn't been queued yet, it has two outstanding refs that both need to be +put. Both DIO read and single-read dispatch fail at this; further, both +differ in the order they do things to the way buffered read works. + +Fix cancellation of both DIO-read and single-read subrequests that failed +preparation by the following steps: + + (1) Harmonise all three reads (buffered, dio, single) to queue the subreq + before prepping it. + + (2) Make all three call netfs_queue_read() to do the queuing. + + (3) Set NETFS_RREQ_ALL_QUEUED independently of the queuing as we don't + know the length of the subreq at this point. + + (4) In all cases, set the error and NETFS_SREQ_FAILED flag on the subreq + and then call netfs_read_subreq_terminated() to deal with it. This + will pass responsibility off to the collector for dealing with it. + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-2-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 34 +++++++++++++------------------- + fs/netfs/direct_read.c | 42 +++++++++++++--------------------------- + fs/netfs/internal.h | 3 +++ + fs/netfs/read_collect.c | 11 +++++++++++ + fs/netfs/read_single.c | 23 ++++++++++------------ + 5 files changed, 50 insertions(+), 63 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index a8c0d86118c58..a27ed501b6d43 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq, + netfs_cache_read_terminated, subreq); + } + +-static void netfs_queue_read(struct netfs_io_request *rreq, +- struct netfs_io_subrequest *subreq, +- bool last_subreq) ++void netfs_queue_read(struct netfs_io_request *rreq, ++ struct netfs_io_subrequest *subreq) + { + struct netfs_io_stream *stream = &rreq->io_streams[0]; + +@@ -178,11 +177,6 @@ static void netfs_queue_read(struct netfs_io_request *rreq, + } + } + +- if (last_subreq) { +- smp_wmb(); /* Write lists before ALL_QUEUED. */ +- set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); +- } +- + spin_unlock(&rreq->lock); + } + +@@ -233,6 +227,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + subreq->start = start; + subreq->len = size; + ++ netfs_queue_read(rreq, subreq); ++ + source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size); + subreq->source = source; + if (source == NETFS_DOWNLOAD_FROM_SERVER) { +@@ -253,6 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + rreq->debug_id, subreq->debug_index, + subreq->len, size, + subreq->start, ictx->zero_point, rreq->i_size); ++ netfs_cancel_read(subreq, ret); + break; + } + subreq->len = len; +@@ -261,12 +258,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + if (rreq->netfs_ops->prepare_read) { + ret = rreq->netfs_ops->prepare_read(subreq); + if (ret < 0) { +- subreq->error = ret; +- /* Not queued - release both refs. */ +- netfs_put_subrequest(subreq, +- netfs_sreq_trace_put_cancel); +- netfs_put_subrequest(subreq, +- netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } + trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); +@@ -289,23 +281,23 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + + pr_err("Unexpected read source %u\n", source); + WARN_ON_ONCE(1); ++ netfs_cancel_read(subreq, ret); + break; + + issue: + slice = netfs_prepare_read_iterator(subreq, ractl); + if (slice < 0) { + ret = slice; +- subreq->error = ret; +- trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); +- /* Not queued - release both refs. */ +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } +- size -= slice; + start += slice; ++ size -= slice; ++ if (size <= 0) { ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); ++ } + +- netfs_queue_read(rreq, subreq, size <= 0); + netfs_issue_read(rreq, subreq); + cond_resched(); + } while (size > 0); +diff --git a/fs/netfs/direct_read.c b/fs/netfs/direct_read.c +index f72e6da88cca7..6a8fb0d55e040 100644 +--- a/fs/netfs/direct_read.c ++++ b/fs/netfs/direct_read.c +@@ -45,12 +45,11 @@ static void netfs_prepare_dio_read_iterator(struct netfs_io_subrequest *subreq) + * Perform a read to a buffer from the server, slicing up the region to be read + * according to the network rsize. + */ +-static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) ++static void netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + { +- struct netfs_io_stream *stream = &rreq->io_streams[0]; + unsigned long long start = rreq->start; + ssize_t size = rreq->len; +- int ret = 0; ++ int ret; + + do { + struct netfs_io_subrequest *subreq; +@@ -58,7 +57,10 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + + subreq = netfs_alloc_subrequest(rreq); + if (!subreq) { +- ret = -ENOMEM; ++ /* Stash the error in the request if there's not ++ * already an error set. ++ */ ++ cmpxchg(&rreq->error, 0, -ENOMEM); + break; + } + +@@ -66,25 +68,13 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + subreq->start = start; + subreq->len = size; + +- __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); +- +- spin_lock(&rreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); +- if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { +- if (!stream->active) { +- stream->collected_to = subreq->start; +- /* Store list pointers before active flag */ +- smp_store_release(&stream->active, true); +- } +- } +- trace_netfs_sreq(subreq, netfs_sreq_trace_added); +- spin_unlock(&rreq->lock); ++ netfs_queue_read(rreq, subreq); + + netfs_stat(&netfs_n_rh_download); + if (rreq->netfs_ops->prepare_read) { + ret = rreq->netfs_ops->prepare_read(subreq); + if (ret < 0) { +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); + break; + } + } +@@ -113,8 +103,6 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + netfs_wake_collector(rreq); + } +- +- return ret; + } + + /* +@@ -137,21 +125,17 @@ static ssize_t netfs_unbuffered_read(struct netfs_io_request *rreq, bool sync) + // TODO: Use bounce buffer if requested + + inode_dio_begin(rreq->inode); ++ netfs_dispatch_unbuffered_reads(rreq); + +- ret = netfs_dispatch_unbuffered_reads(rreq); +- +- if (!rreq->submitted) { +- netfs_put_request(rreq, netfs_rreq_trace_put_no_submit); +- inode_dio_end(rreq->inode); +- ret = 0; +- goto out; +- } ++ /* The collector will get run, even if we don't manage to submit any ++ * subreqs, so we shouldn't call inode_dio_end() here. ++ */ + + if (sync) + ret = netfs_wait_for_read(rreq); + else + ret = -EIOCBQUEUED; +-out: ++ + _leave(" = %zd", ret); + return ret; + } +diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h +index d436e20d34185..645996ecfc803 100644 +--- a/fs/netfs/internal.h ++++ b/fs/netfs/internal.h +@@ -23,6 +23,8 @@ + /* + * buffered_read.c + */ ++void netfs_queue_read(struct netfs_io_request *rreq, ++ struct netfs_io_subrequest *subreq); + void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); + int netfs_prefetch_for_write(struct file *file, struct folio *folio, + size_t offset, size_t len); +@@ -108,6 +110,7 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq, + */ + bool netfs_read_collection(struct netfs_io_request *rreq); + void netfs_read_collection_worker(struct work_struct *work); ++void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); + void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); + + /* +diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c +index e5f6665b3341e..d2d902f466271 100644 +--- a/fs/netfs/read_collect.c ++++ b/fs/netfs/read_collect.c +@@ -575,6 +575,17 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) + } + EXPORT_SYMBOL(netfs_read_subreq_terminated); + ++/* ++ * Cancel a read subrequest due to preparation failure. ++ */ ++void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error) ++{ ++ trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); ++ subreq->error = error; ++ __set_bit(NETFS_SREQ_FAILED, &subreq->flags); ++ netfs_read_subreq_terminated(subreq); ++} ++ + /* + * Handle termination of a read from the cache. + */ +diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c +index d0e23bc42445f..8833550d2eb60 100644 +--- a/fs/netfs/read_single.c ++++ b/fs/netfs/read_single.c +@@ -89,7 +89,6 @@ static void netfs_single_read_cache(struct netfs_io_request *rreq, + */ + static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + { +- struct netfs_io_stream *stream = &rreq->io_streams[0]; + struct netfs_io_subrequest *subreq; + int ret = 0; + +@@ -102,14 +101,7 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + subreq->len = rreq->len; + subreq->io_iter = rreq->buffer.iter; + +- __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); +- +- spin_lock(&rreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); +- trace_netfs_sreq(subreq, netfs_sreq_trace_added); +- /* Store list pointers before active flag */ +- smp_store_release(&stream->active, true); +- spin_unlock(&rreq->lock); ++ netfs_queue_read(rreq, subreq); + + netfs_single_cache_prepare_read(rreq, subreq); + switch (subreq->source) { +@@ -121,10 +113,14 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + goto cancel; + } + ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + rreq->netfs_ops->issue_read(subreq); + rreq->submitted += subreq->len; + break; + case NETFS_READ_FROM_CACHE: ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + trace_netfs_sreq(subreq, netfs_sreq_trace_submit); + netfs_single_read_cache(rreq, subreq); + rreq->submitted += subreq->len; +@@ -134,14 +130,15 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) + pr_warn("Unexpected single-read source %u\n", subreq->source); + WARN_ON_ONCE(true); + ret = -EIO; +- break; ++ goto cancel; + } + +- smp_wmb(); /* Write lists before ALL_QUEUED. */ +- set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + return ret; + cancel: +- netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); ++ netfs_cancel_read(subreq, ret); ++ smp_wmb(); /* Write lists before ALL_QUEUED. */ ++ set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); ++ netfs_wake_collector(rreq); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch b/queue-7.0/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch new file mode 100644 index 0000000000..5a268a30c6 --- /dev/null +++ b/queue-7.0/netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch @@ -0,0 +1,82 @@ +From 0bd0524e0a9e00158e97e0ab8bc05e74d6c0a810 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:54 +0100 +Subject: netfs: Fix early put of sink folio in netfs_read_gaps() + +From: David Howells + +[ Upstream commit 3e5dd91b87a8b1450217b56a336bee315f40da7d ] + +Fix netfs_read_gaps() to release the sink page it uses after waiting for +the request to complete. The way the sink page is used is that an +ITER_BVEC-class iterator is created that has the gaps from the target folio +at either end, but has the sink page tiled over the middle so that a single +read op can fill in both gaps. + +The bug was found by KASAN detecting a UAF on the generic/075 xfstest in +the cifsd kernel thread that handles reception of data from the TCP socket: + + BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 + Write of size 885 at addr ffff888107f92000 by task cifsd/1285 + CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) + Call Trace: + dump_stack_lvl+0x5d/0x80 + print_report+0x17f/0x4f1 + kasan_report+0x100/0x1e0 + kasan_check_range+0x10f/0x1e0 + __asan_memcpy+0x3c/0x60 + _copy_to_iter+0x48a/0xa20 + __skb_datagram_iter+0x2c9/0x430 + skb_copy_datagram_iter+0x6e/0x160 + tcp_recvmsg_locked+0xce0/0x1130 + tcp_recvmsg+0xeb/0x300 + inet_recvmsg+0xcf/0x3a0 + sock_recvmsg+0xea/0x100 + cifs_readv_from_socket+0x3a6/0x4d0 [cifs] + cifs_read_iter_from_socket+0xdd/0x130 [cifs] + cifs_readv_receive+0xaad/0xb10 [cifs] + cifs_demultiplex_thread+0x1148/0x1740 [cifs] + kthread+0x1cf/0x210 + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Reported-by: Steve French +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 51f844bfbdff6..e7ad511e494cc 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -457,9 +457,6 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + + netfs_read_to_pagecache(rreq, NULL); + +- if (sink) +- folio_put(sink); +- + ret = netfs_wait_for_read(rreq); + if (ret >= 0) { + if (group) +@@ -471,6 +468,9 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + flush_dcache_folio(folio); + folio_mark_uptodate(folio); + } ++ ++ if (sink) ++ folio_put(sink); + folio_unlock(folio); + netfs_put_request(rreq, netfs_rreq_trace_put_return); + return ret < 0 ? ret : 0; +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch b/queue-7.0/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch new file mode 100644 index 0000000000..aa02a86c6a --- /dev/null +++ b/queue-7.0/netfs-fix-folio-private-handling-in-netfs_perform_wr.patch @@ -0,0 +1,307 @@ +From 649c8c31fd6698cd93cdbe347d407896f22c36f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:58 +0100 +Subject: netfs: Fix folio->private handling in netfs_perform_write() + +From: David Howells + +[ Upstream commit ccde2ac757c713535b224233a296de40efe5212d ] + +Under some circumstances, netfs_perform_write() doesn't correctly +manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing +to a group and pointing to a netfs_folio struct, leading to potential +multiple attachments of private data with associated folio ref leaks and +also leaks of netfs_folio structs or netfs_group refs. + +Fix this by consolidating the place at which a folio is marked uptodate in +one place and having that look at what's attached to folio->private and +decide how to clean it up and then set the new group. Also, the content +shouldn't be flushed if group is NULL, even if a group is specified in the +netfs_group parameter, as that would be the case for a new folio. A +filesystem should always specify netfs_group or never specify netfs_group. + +The Sashiko auto-review tool noted that it was theoretically possible that +the fpos >= ctx->zero_point section might leak if it modified a streaming +write folio. This is unlikely, but with a network filesystem, third party +changes can happen. It also pointed out that __netfs_set_group() would +leak if called multiple times on the same folio from the "whole folio +modify section". + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 134 +++++++++++++++++++++-------------- + include/trace/events/netfs.h | 1 + + 2 files changed, 82 insertions(+), 53 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index b606c3bd84bcd..dee10570383bb 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -13,24 +13,6 @@ + #include + #include "internal.h" + +-static void __netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- if (netfs_group) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +-} +- +-static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) +-{ +- void *priv = folio_get_private(folio); +- +- if (unlikely(priv != netfs_group)) { +- if (netfs_group && (!priv || priv == NETFS_FOLIO_COPY_TO_CACHE)) +- folio_attach_private(folio, netfs_get_group(netfs_group)); +- else if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) +- folio_detach_private(folio); +- } +-} +- + /* + * Grab a folio for writing and lock it. Attempt to allocate as large a folio + * as possible to hold as much of the remaining length as possible in one go. +@@ -158,6 +140,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + size_t offset; /* Offset into pagecache folio */ + size_t part; /* Bytes to write to folio */ + size_t copied; /* Bytes copied from user */ ++ void *priv; + + offset = pos & (max_chunk - 1); + part = min(max_chunk - offset, iov_iter_count(iter)); +@@ -203,6 +186,25 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto error_folio_unlock; + } + ++ finfo = netfs_folio_info(folio); ++ group = netfs_folio_group(folio); ++ ++ /* If the requested group differs from the group set on the ++ * page, then we need to flush out the folio if it has a group ++ * set (ie. is non-NULL). Note that COPY_TO_CACHE is a special ++ * case, being a netfs annotation rather than an actual group. ++ * ++ * The filesystem isn't permitted to mix writes with groups and ++ * writes without groups as the NULL group is used to indicate ++ * that no group is set. ++ */ ++ if (unlikely(group != netfs_group) && ++ group != NETFS_FOLIO_COPY_TO_CACHE && ++ group) { ++ WARN_ON_ONCE(!netfs_group); ++ goto flush_content; ++ } ++ + /* Decide how we should modify a folio. We might be attempting + * to do write-streaming, as we don't want to a local RMW cycle + * if we can avoid it. If we're doing local caching or content +@@ -210,22 +212,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + * file is open readably, then we let ->read_folio() fill in + * the gaps. + */ +- finfo = netfs_folio_info(folio); +- group = netfs_folio_group(folio); +- +- if (unlikely(group != netfs_group) && +- group != NETFS_FOLIO_COPY_TO_CACHE) +- goto flush_content; +- + if (folio_test_uptodate(folio)) { + if (mapping_writably_mapped(mapping)) + flush_dcache_folio(folio); + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_folio_is_uptodate; +- goto copied; ++ goto copied_uptodate; + } + + /* If the page is above the zero-point then we assume that the +@@ -238,24 +232,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + folio_zero_segment(folio, offset + copied, flen); +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_modify_and_clear; +- goto copied; ++ if (finfo) ++ trace = netfs_modify_and_clear_rm_finfo; ++ else ++ trace = netfs_modify_and_clear; ++ goto mark_uptodate; + } + + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (likely(copied == part)) { +- if (finfo) { ++ if (finfo) + trace = netfs_whole_folio_modify_filled; +- goto folio_now_filled; +- } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; +- goto copied; ++ else ++ trace = netfs_whole_folio_modify; ++ goto mark_uptodate; + } + if (copied == 0) + goto copy_failed; +@@ -273,7 +265,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += finfo->dirty_offset; + if (finfo->dirty_len == flen) { + trace = netfs_whole_folio_modify_filled_efault; +- goto folio_now_filled; ++ goto mark_uptodate; + } + if (copied > finfo->dirty_len) + finfo->dirty_len = copied; +@@ -301,11 +293,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) + goto copy_failed; +- netfs_set_group(folio, netfs_group); + trace = netfs_just_prefetch; +- goto copied; ++ goto copied_uptodate; + } + ++ /* Do a streaming write on a folio that has nothing in it yet. */ + if (!finfo) { + ret = -EIO; + if (WARN_ON(folio_get_private(folio))) +@@ -314,10 +306,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + if (unlikely(copied == 0)) + goto copy_failed; + if (offset == 0 && copied == flen) { +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); + trace = netfs_streaming_filled_page; +- goto copied; ++ goto mark_uptodate; + } + + finfo = kzalloc_obj(*finfo); +@@ -346,7 +336,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { + trace = netfs_streaming_cont_filled_page; +- goto folio_now_filled; ++ goto mark_uptodate; + } + trace = netfs_streaming_write_cont; + goto copied; +@@ -362,13 +352,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + +- folio_now_filled: +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); ++ /* Mark a folio as being up to data when we've filled it ++ * completely. If the folio has a group attached, then it must ++ * be the same group, otherwise we should have flushed it out ++ * above. We have to get rid of the netfs_folio struct if ++ * there was one. ++ */ ++ mark_uptodate: + folio_mark_uptodate(folio); +- kfree(finfo); ++ ++ copied_uptodate: ++ priv = folio_get_private(folio); ++ if (likely(priv == netfs_group)) { ++ /* Already set correctly; no change required. */ ++ } else if (priv == NETFS_FOLIO_COPY_TO_CACHE) { ++ if (!netfs_group) ++ folio_detach_private(folio); ++ else ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ } else if (!priv) { ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ } else { ++ WARN_ON_ONCE(!finfo); ++ if (netfs_group) ++ /* finfo->netfs_group has a ref */ ++ folio_change_private(folio, netfs_group); ++ else ++ folio_detach_private(folio); ++ kfree(finfo); ++ } ++ + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +@@ -531,6 +544,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + struct inode *inode = file_inode(file); + struct netfs_inode *ictx = netfs_inode(inode); + vm_fault_t ret = VM_FAULT_NOPAGE; ++ void *priv; + int err; + + _enter("%lx", folio->index); +@@ -551,7 +565,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + } + + group = netfs_folio_group(folio); +- if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) { ++ if (group && ++ group != netfs_group && ++ group != NETFS_FOLIO_COPY_TO_CACHE) { + folio_unlock(folio); + err = filemap_fdatawrite_range(mapping, + folio_pos(folio), +@@ -573,7 +589,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr + trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus); + else + trace_netfs_folio(folio, netfs_folio_trace_mkwrite); +- netfs_set_group(folio, netfs_group); ++ ++ priv = folio_get_private(folio); ++ if (priv != netfs_group) { ++ if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_detach_private(folio); ++ else if (netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) ++ folio_change_private(folio, netfs_get_group(netfs_group)); ++ else if (netfs_group && !priv) ++ folio_attach_private(folio, netfs_get_group(netfs_group)); ++ else ++ WARN_ON_ONCE(1); ++ } ++ + file_update_time(file); + set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); + if (ictx->ops->post_modify) +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index db045135406c9..3fe3980902c24 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -181,6 +181,7 @@ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ ++ EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ + EM(netfs_flush_content, "flush") \ +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch b/queue-7.0/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch new file mode 100644 index 0000000000..7cb749c596 --- /dev/null +++ b/queue-7.0/netfs-fix-leak-of-request-in-netfs_write_begin-error.patch @@ -0,0 +1,44 @@ +From b012a781bf495c80479b2b74d2713c367af07c53 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:55 +0100 +Subject: netfs: Fix leak of request in netfs_write_begin() error handling + +From: David Howells + +[ Upstream commit 5046a34f0643441f05b0253ea64e1a3af87efe14 ] + +Fix netfs_write_begin() to not leak our ref on the request in the event +that we get an error from netfs_wait_for_read(). + +Fixes: 4090b31422a6 ("netfs: Add a function to consolidate beginning a read") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-19-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index e7ad511e494cc..004d426c02b41 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -687,9 +687,9 @@ int netfs_write_begin(struct netfs_inode *ctx, + + netfs_read_to_pagecache(rreq, NULL); + ret = netfs_wait_for_read(rreq); ++ netfs_put_request(rreq, netfs_rreq_trace_put_return); + if (ret < 0) + goto error; +- netfs_put_request(rreq, netfs_rreq_trace_put_return); + + have_folio: + ret = folio_wait_private_2_killable(folio); +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-missing-barriers-when-accessing-stream-sub.patch b/queue-7.0/netfs-fix-missing-barriers-when-accessing-stream-sub.patch new file mode 100644 index 0000000000..0b4d05d3f9 --- /dev/null +++ b/queue-7.0/netfs-fix-missing-barriers-when-accessing-stream-sub.patch @@ -0,0 +1,184 @@ +From b5346a3c66913d4c95954d28e3f5151f2ffe1be3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:40 +0100 +Subject: netfs: Fix missing barriers when accessing stream->subrequests + locklessly + +From: David Howells + +[ Upstream commit b5782e2d462c028096f922abca46318cec890670 ] + +The list of subrequests attached to stream->subrequests is accessed without +locks by netfs_collect_read_results() and netfs_collect_write_results(), +and then they access subreq->flags without taking a barrier after getting +the subreq pointer from the list. Relatedly, the functions that build the +list don't use any sort of write barrier when constructing the list to make +sure that the NETFS_SREQ_IN_PROGRESS flag is perceived to be set first if +no lock is taken. + +Fix this by: + + (1) Add a new list_add_tail_release() function that uses a release barrier + to set the pointer to the new member of the list. + + (2) Add a new list_first_entry_or_null_acquire() function that uses an + acquire barrier to read the pointer to the first member in a list (or + return NULL). + + (3) Use list_add_tail_release() when adding a subreq to ->subrequests. + + (4) Use list_first_entry_or_null_acquire() when initially accessing the + front of the list (when an item is removed, the pointer to the new + front iterm is obtained under the same lock). + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Fixes: 288ace2f57c9 ("netfs: New writeback implementation") +Link: https://sashiko.dev/#/patchset/20260326104544.509518-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-4-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 3 ++- + fs/netfs/misc.c | 1 + + fs/netfs/read_collect.c | 6 ++++-- + fs/netfs/write_collect.c | 6 ++++-- + fs/netfs/write_issue.c | 3 ++- + include/linux/list.h | 37 +++++++++++++++++++++++++++++++++++++ + 6 files changed, 50 insertions(+), 6 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index a27ed501b6d43..15d73026ff643 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -168,7 +168,8 @@ void netfs_queue_read(struct netfs_io_request *rreq, + * remove entries off of the front. + */ + spin_lock(&rreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); ++ /* Write IN_PROGRESS before pointer to new subreq */ ++ list_add_tail_release(&subreq->rreq_link, &stream->subrequests); + if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { + if (!stream->active) { + stream->collected_to = subreq->start; +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 6df89c92b10b0..21357907b7eee 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -356,6 +356,7 @@ void netfs_wait_for_in_progress_stream(struct netfs_io_request *rreq, + DEFINE_WAIT(myself); + + list_for_each_entry(subreq, &stream->subrequests, rreq_link) { ++ smp_rmb(); /* Read ->next before IN_PROGRESS. */ + if (!netfs_check_subreq_in_progress(subreq)) + continue; + +diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c +index d2d902f466271..3c9b847885c2a 100644 +--- a/fs/netfs/read_collect.c ++++ b/fs/netfs/read_collect.c +@@ -205,8 +205,10 @@ static void netfs_collect_read_results(struct netfs_io_request *rreq) + * in progress. The issuer thread may be adding stuff to the tail + * whilst we're doing this. + */ +- front = list_first_entry_or_null(&stream->subrequests, +- struct netfs_io_subrequest, rreq_link); ++ front = list_first_entry_or_null_acquire(&stream->subrequests, ++ struct netfs_io_subrequest, rreq_link); ++ /* Read first subreq pointer before IN_PROGRESS flag. */ ++ + while (front) { + size_t transferred; + +diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c +index b194447f4b111..7fbf50907a7fc 100644 +--- a/fs/netfs/write_collect.c ++++ b/fs/netfs/write_collect.c +@@ -228,8 +228,10 @@ static void netfs_collect_write_results(struct netfs_io_request *wreq) + if (!smp_load_acquire(&stream->active)) + continue; + +- front = list_first_entry_or_null(&stream->subrequests, +- struct netfs_io_subrequest, rreq_link); ++ front = list_first_entry_or_null_acquire(&stream->subrequests, ++ struct netfs_io_subrequest, rreq_link); ++ /* Read first subreq pointer before IN_PROGRESS flag. */ ++ + while (front) { + trace_netfs_collect_sreq(wreq, front); + //_debug("sreq [%x] %llx %zx/%zx", +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 2db688f941251..b0e9690bb90ce 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -204,7 +204,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq, + * remove entries off of the front. + */ + spin_lock(&wreq->lock); +- list_add_tail(&subreq->rreq_link, &stream->subrequests); ++ /* Write IN_PROGRESS before pointer to new subreq */ ++ list_add_tail_release(&subreq->rreq_link, &stream->subrequests); + if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { + if (!stream->active) { + stream->collected_to = subreq->start; +diff --git a/include/linux/list.h b/include/linux/list.h +index 00ea8e5fb88b0..09d979976b3b8 100644 +--- a/include/linux/list.h ++++ b/include/linux/list.h +@@ -191,6 +191,29 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head) + __list_add(new, head->prev, head); + } + ++/** ++ * list_add_tail_release - add a new entry with release barrier ++ * @new: new entry to be added ++ * @head: list head to add it before ++ * ++ * Insert a new entry before the specified head, using a release barrier to set ++ * the ->next pointer that points to it. This is useful for implementing ++ * queues, in particular one that the elements will be walked through forwards ++ * locklessly. ++ */ ++static inline void list_add_tail_release(struct list_head *new, ++ struct list_head *head) ++{ ++ struct list_head *prev = head->prev; ++ ++ if (__list_add_valid(new, prev, head)) { ++ new->next = head; ++ new->prev = prev; ++ head->prev = new; ++ smp_store_release(&prev->next, new); ++ } ++} ++ + /* + * Delete a list entry by making the prev/next entries + * point to each other. +@@ -644,6 +667,20 @@ static inline void list_splice_tail_init(struct list_head *list, + pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ + }) + ++/** ++ * list_first_entry_or_null_acquire - get the first element from a list with barrier ++ * @ptr: the list head to take the element from. ++ * @type: the type of the struct this is embedded in. ++ * @member: the name of the list_head within the struct. ++ * ++ * Note that if the list is empty, it returns NULL. ++ */ ++#define list_first_entry_or_null_acquire(ptr, type, member) ({ \ ++ struct list_head *head__ = (ptr); \ ++ struct list_head *pos__ = smp_load_acquire(&head__->next); \ ++ pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ ++}) ++ + /** + * list_last_entry_or_null - get the last element from a list + * @ptr: the list head to take the element from. +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-missing-locking-around-retry-adding-new-su.patch b/queue-7.0/netfs-fix-missing-locking-around-retry-adding-new-su.patch new file mode 100644 index 0000000000..ce5b055cf3 --- /dev/null +++ b/queue-7.0/netfs-fix-missing-locking-around-retry-adding-new-su.patch @@ -0,0 +1,83 @@ +From 117011aa9b0b2570effa24a78a29f2a058a8e89b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:39 +0100 +Subject: netfs: Fix missing locking around retry adding new subreqs + +From: David Howells + +[ Upstream commit cce18c263e9623872327ba3c956012f73c1179cc ] + +Fix netfs_retry_read_subrequests() and netfs_retry_write_stream() to take +the appropriate lock when adding extra subrequests into +stream->subrequests. + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Fixes: 288ace2f57c9 ("netfs: New writeback implementation") +Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-3-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/read_retry.c | 6 +++++- + fs/netfs/write_retry.c | 6 +++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index cca9ac43c0773..5ec548b996d65 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -175,7 +175,9 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq) + list_for_each_entry_safe_from(subreq, tmp, + &stream->subrequests, rreq_link) { + trace_netfs_sreq(subreq, netfs_sreq_trace_superfluous); ++ spin_lock(&rreq->lock); + list_del(&subreq->rreq_link); ++ spin_unlock(&rreq->lock); + netfs_put_subrequest(subreq, netfs_sreq_trace_put_done); + if (subreq == to) + break; +@@ -203,8 +205,10 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq) + refcount_read(&subreq->ref), + netfs_sreq_trace_new); + ++ spin_lock(&rreq->lock); + list_add(&subreq->rreq_link, &to->rreq_link); +- to = list_next_entry(to, rreq_link); ++ spin_unlock(&rreq->lock); ++ to = subreq; + trace_netfs_sreq(subreq, netfs_sreq_trace_retry); + + stream->sreq_max_len = umin(len, rreq->rsize); +diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c +index 29489a23a2209..32735abfa03f0 100644 +--- a/fs/netfs/write_retry.c ++++ b/fs/netfs/write_retry.c +@@ -130,7 +130,9 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, + list_for_each_entry_safe_from(subreq, tmp, + &stream->subrequests, rreq_link) { + trace_netfs_sreq(subreq, netfs_sreq_trace_discard); ++ spin_lock(&wreq->lock); + list_del(&subreq->rreq_link); ++ spin_unlock(&wreq->lock); + netfs_put_subrequest(subreq, netfs_sreq_trace_put_done); + if (subreq == to) + break; +@@ -153,8 +155,10 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, + netfs_sreq_trace_new); + trace_netfs_sreq(subreq, netfs_sreq_trace_split); + ++ spin_lock(&wreq->lock); + list_add(&subreq->rreq_link, &to->rreq_link); +- to = list_next_entry(to, rreq_link); ++ spin_unlock(&wreq->lock); ++ to = subreq; + trace_netfs_sreq(subreq, netfs_sreq_trace_retry); + + stream->sreq_max_len = len; +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch b/queue-7.0/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch new file mode 100644 index 0000000000..49ac5966d7 --- /dev/null +++ b/queue-7.0/netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch @@ -0,0 +1,128 @@ +From 842ccc7439f68cc931b8de7d19aba071adc73a0f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:48 +0100 +Subject: netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes + gone + +From: David Howells + +[ Upstream commit 156ac2ec2ee77c44c4eb7439d6d165247ba12247 ] + +If a streaming write is made, this will leave the relevant modified folio +in a not-uptodate, but dirty state with a netfs_folio struct hung off of +folio->private indicating the dirty range. Subsequently truncating the +file such that the dirty data in the folio is removed, but the first part +of the folio theoretically remains will cause the netfs_folio struct to be +discarded... but will leave the dirty flag set. + +If the folio is then read via mmap(), netfs_read_folio() will see that the +page is dirty and jump to netfs_read_gaps() to fill in the missing bits. +netfs_read_gaps(), however, expects there to be a netfs_folio struct +present and can oops because truncate removed it. + +Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the +event that all the dirty data in the folio is erased (as nfs does). + +Also add some tracepoints to log modifications to a dirty page. + +This can be reproduced with something like: + + dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1 + umount /xfstest.test + mount /xfstest.test + xfs_io -c "w 0xbbbf 0xf96c" \ + -c "truncate 0xbbbf" \ + -c "mmap -r 0xb000 0x11000" \ + -c "mr 0xb000 0x11000" \ + /xfstest.test/foo + +with fscaching disabled (otherwise streaming writes are suppressed) and a +change to netfs_perform_write() to disallow streaming writes if the fd is +open O_RDWR: + + if (//(file->f_mode & FMODE_READ) || <--- comment this out + netfs_is_cache_enabled(ctx)) { + +It should be reproducible even without this change, but if prevents the +above trivial xfs_io command from reproducing it. + +Note that the initial dd is important: the file must start out sufficiently +large that the zero-point logic doesn't just clear the gaps because it +knows there's nothing in the file to read yet. Unmounting and mounting is +needed to clear the pagecache (there are other ways to do that that may +also work). + +This was initially reproduced with the generic/522 xfstest on some patches +that remove the FMODE_READ restriction. + +Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write") +Reported-by: Marc Dionne +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 6 +++++- + include/trace/events/netfs.h | 4 ++++ + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 723571ca1b885..24b20e80e9a8a 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -263,6 +263,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; + finfo->dirty_offset = offset; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } + +@@ -271,12 +272,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + */ + if (iend >= fend) { + finfo->dirty_len = offset - fstart; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_tail); + return; + } + + /* A partial write was split. The caller has already zeroed + * it, so just absorb the hole. + */ ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_middle); + } + return; + +@@ -284,8 +287,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + netfs_put_group(netfs_folio_group(folio)); + folio_detach_private(folio); + folio_clear_uptodate(folio); ++ folio_cancel_dirty(folio); + kfree(finfo); +- return; ++ trace_netfs_folio(folio, netfs_folio_trace_invalidate_all); + } + EXPORT_SYMBOL(netfs_invalidate_folio); + +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index cbe28211106c5..88d814ba1e697 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -194,6 +194,10 @@ + EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ + EM(netfs_folio_trace_end_copy, "end-copy") \ + EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ ++ EM(netfs_folio_trace_invalidate_all, "inval-all") \ ++ EM(netfs_folio_trace_invalidate_front, "inval-front") \ ++ EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ ++ EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ + EM(netfs_folio_trace_kill, "kill") \ + EM(netfs_folio_trace_kill_cc, "kill-cc") \ + EM(netfs_folio_trace_kill_g, "kill-g") \ +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch b/queue-7.0/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch new file mode 100644 index 0000000000..c1f6a12da0 --- /dev/null +++ b/queue-7.0/netfs-fix-netfs_read_folio-to-wait-on-writeback.patch @@ -0,0 +1,44 @@ +From a0ea356af5404b2f593dacd7079a349f92a0fb5d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:59 +0100 +Subject: netfs: Fix netfs_read_folio() to wait on writeback + +From: David Howells + +[ Upstream commit ded0c6f1606061148c202825f7e53d711f9f84cf ] + +Fix netfs_read_folio() to wait for an ongoing writeback to complete so that +it can trust the dirty flag and whatever is attached to folio->private +(folio->private may get cleaned up by the collector before it clears the +writeback flag). + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-23-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 83d0b8153e96e..76d0f6a29abab 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -503,6 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) + struct netfs_inode *ctx = netfs_inode(mapping->host); + int ret; + ++ folio_wait_writeback(folio); ++ + if (folio_test_dirty(folio)) + return netfs_read_gaps(file, folio); + +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch b/queue-7.0/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch new file mode 100644 index 0000000000..f6d39f28c2 --- /dev/null +++ b/queue-7.0/netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch @@ -0,0 +1,44 @@ +From c2532420d481ce31028129917c9c483f1af76fb2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:41 +0100 +Subject: netfs: Fix netfs_read_to_pagecache() to pause on subreq failure + +From: David Howells + +[ Upstream commit 8a8c0cfdf4658fc5b295b7fc87be56e0d76741f4 ] + +Fix netfs_read_to_pagecache() so that it pauses the generation of new +subrequests if an already-issued subrequest fails. + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-5-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 15d73026ff643..fee0aebf5a3d6 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -300,6 +300,11 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + } + + netfs_issue_read(rreq, subreq); ++ ++ if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) ++ netfs_wait_for_paused_read(rreq); ++ if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) ++ break; + cond_resched(); + } while (size > 0); + +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch b/queue-7.0/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch new file mode 100644 index 0000000000..888b3b11fc --- /dev/null +++ b/queue-7.0/netfs-fix-overrun-check-in-netfs_extract_user_iter.patch @@ -0,0 +1,80 @@ +From f1c4af3330a5da9dda677ba3d13ec9e44f01d2b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:47 +0100 +Subject: netfs: Fix overrun check in netfs_extract_user_iter() + +From: David Howells + +[ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ] + +Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills +pages[], then those pages don't get included in the iterator constructed at +the end of the function. If there was an overfill, memory corruption has +already happened. + +Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into a BVEC iterator") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/iterator.c | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c +index 429e4396e1b00..b375567e0520e 100644 +--- a/fs/netfs/iterator.c ++++ b/fs/netfs/iterator.c +@@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + break; + } + +- if (ret > count) { +- pr_err("get_pages rc=%zd more than %zu\n", ret, count); ++ if (WARN(ret > count, ++ "%s: extract_pages overrun %zd > %zu bytes\n", ++ __func__, ret, count)) { ++ ret = -EIO; + break; + } + +- count -= ret; +- ret += offset; +- cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE); +- +- if (npages + cur_npages > max_pages) { +- pr_err("Out of bvec array capacity (%u vs %u)\n", +- npages + cur_npages, max_pages); ++ cur_npages = DIV_ROUND_UP(offset + ret, PAGE_SIZE); ++ if (WARN(cur_npages > max_pages - npages, ++ "%s: extract_pages overrun %u > %u pages\n", ++ __func__, npages + cur_npages, max_pages)) { ++ ret = -EIO; + break; + } + ++ count -= ret; ++ ret += offset; ++ + for (i = 0; i < cur_npages; i++) { + len = ret > PAGE_SIZE ? PAGE_SIZE : ret; + bvec_set_page(bv + npages + i, *pages++, len - offset, offset); +@@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, + npages += cur_npages; + } + ++ /* Note: Don't try to clean up after EIO. Either we got no pages, so ++ * nothing to clean up, or we got a buffer overrun, memory corruption ++ * and can't trust the stuff in the buffer (a WARN was emitted). ++ */ ++ + if (ret < 0 && (ret == -ENOMEM || npages == 0)) { + for (i = 0; i < npages; i++) + unpin_user_page(bv[i].bv_page); +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-partial-invalidation-of-streaming-write-fo.patch b/queue-7.0/netfs-fix-partial-invalidation-of-streaming-write-fo.patch new file mode 100644 index 0000000000..f11251ffcd --- /dev/null +++ b/queue-7.0/netfs-fix-partial-invalidation-of-streaming-write-fo.patch @@ -0,0 +1,49 @@ +From 4528b59914432811499d716070508507db52b504 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:57 +0100 +Subject: netfs: Fix partial invalidation of streaming-write folio + +From: David Howells + +[ Upstream commit 6d91acc7fb85d33ea58fca9b964a32a453937f4b ] + +In netfs_invalidate_folio(), if the region of a partial invalidation +overlaps the front (but not all) of a dirty write cached in a streaming +write page (dirty, but not uptodate, with the dirty region tracked by a +netfs_folio struct), the function modifies the dirty region - but +incorrectly as it moves the region forward by setting the start to the +start, not the end, of the invalidation region. + +Fix this by setting finfo->dirty_offset to the end of the invalidation +region (iend). + +Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netfs_inval_folio()") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 24b20e80e9a8a..5d554512ed23a 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -262,7 +262,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + goto erase_completely; + /* Move the start of the data. */ + finfo->dirty_len = fend - iend; +- finfo->dirty_offset = offset; ++ finfo->dirty_offset = iend; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); + return; + } +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-potential-deadlock-in-write-through-mode.patch b/queue-7.0/netfs-fix-potential-deadlock-in-write-through-mode.patch new file mode 100644 index 0000000000..a22095f867 --- /dev/null +++ b/queue-7.0/netfs-fix-potential-deadlock-in-write-through-mode.patch @@ -0,0 +1,119 @@ +From 1ec6073d6384587bd68c4f89161d4bd1590ce09b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:51 +0100 +Subject: netfs: Fix potential deadlock in write-through mode + +From: David Howells + +[ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ] + +Fix netfs_advance_writethrough() to always unlock the supplied folio and to +mark it dirty if it isn't yet written to the end. Unfortunately, it can't +be marked for writeback until the folio is done with as that may cause a +deadlock against mmapped reads and writes. + +Even though it has been marked dirty, premature writeback can't occur as +the caller is holding both inode->i_rwsem (which will prevent concurrent +truncation, fallocation, DIO and other writes) and ictx->wb_lock (which +will cause flushing to wait and writeback to skip or wait). + +Note that this may be easier to deal with once the queuing of folios is +split from the generation of subrequests. + +Fixes: 288ace2f57c9 ("netfs: New writeback implementation") +Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/write_issue.c | 39 +++++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 14 deletions(-) + +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index b0e9690bb90ce..03961622996be 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -414,12 +414,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, + if (streamw) + netfs_issue_write(wreq, cache); + +- /* Flip the page to the writeback state and unlock. If we're called +- * from write-through, then the page has already been put into the wb +- * state. +- */ +- if (wreq->origin == NETFS_WRITEBACK) +- folio_start_writeback(folio); ++ folio_start_writeback(folio); + folio_unlock(folio); + + if (fgroup == NETFS_FOLIO_COPY_TO_CACHE) { +@@ -647,29 +642,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c + struct folio *folio, size_t copied, bool to_page_end, + struct folio **writethrough_cache) + { ++ int ret; ++ + _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u", + wreq->debug_id, wreq->buffer.iter.count, wreq->wsize, copied, to_page_end); + +- if (!*writethrough_cache) { +- if (folio_test_dirty(folio)) +- /* Sigh. mmap. */ +- folio_clear_dirty_for_io(folio); ++ /* The folio is locked. */ + ++ if (*writethrough_cache != folio) { ++ if (*writethrough_cache) { ++ /* Did the folio get moved? */ ++ folio_put(*writethrough_cache); ++ *writethrough_cache = NULL; ++ } + /* We can make multiple writes to the folio... */ +- folio_start_writeback(folio); + if (wreq->len == 0) + trace_netfs_folio(folio, netfs_folio_trace_wthru); + else + trace_netfs_folio(folio, netfs_folio_trace_wthru_plus); + *writethrough_cache = folio; ++ folio_get(folio); + } + + wreq->len += copied; +- if (!to_page_end) ++ ++ if (!to_page_end) { ++ folio_mark_dirty(folio); ++ folio_unlock(folio); + return 0; ++ } + ++ ret = netfs_write_folio(wreq, wbc, folio); ++ folio_put(*writethrough_cache); + *writethrough_cache = NULL; +- return netfs_write_folio(wreq, wbc, folio); ++ wreq->submitted = wreq->len; ++ return ret; + } + + /* +@@ -683,8 +690,12 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c + + _enter("R=%x", wreq->debug_id); + +- if (writethrough_cache) ++ if (writethrough_cache) { ++ folio_lock(writethrough_cache); + netfs_write_folio(wreq, wbc, writethrough_cache); ++ folio_put(writethrough_cache); ++ wreq->submitted = wreq->len; ++ } + + netfs_end_issue_write(wreq); + +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-potential-for-tearing-in-remote_i_size-and.patch b/queue-7.0/netfs-fix-potential-for-tearing-in-remote_i_size-and.patch new file mode 100644 index 0000000000..d96dd14f00 --- /dev/null +++ b/queue-7.0/netfs-fix-potential-for-tearing-in-remote_i_size-and.patch @@ -0,0 +1,1140 @@ +From 5e1b996297965fd302dfb2f5ab5fdda49e938744 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:42 +0100 +Subject: netfs: Fix potential for tearing in ->remote_i_size and ->zero_point + +From: David Howells + +[ Upstream commit 2c8f4742bb76117d735f92a3932d85239b16c494 ] + +Fix potential tearing in using ->remote_i_size and ->zero_point by copying +i_size_read() and i_size_write() and using the same seqcount as for i_size. + +We need to make sure that netfslib and the filesystems that use it always +hold i_lock whilst updating any of the sizes to prevent i_size_seqcount +from getting corrupted. + +Fixes: 4058f742105e ("netfs: Keep track of the actual remote file size") +Fixes: 100ccd18bb41 ("netfs: Optimise away reads above the point at which there can be no data") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-6-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/9p/v9fs_vfs.h | 13 -- + fs/9p/vfs_inode.c | 6 +- + fs/9p/vfs_inode_dotl.c | 12 +- + fs/afs/file.c | 24 +++- + fs/afs/inode.c | 31 ++-- + fs/afs/internal.h | 11 +- + fs/afs/write.c | 2 +- + fs/netfs/buffered_read.c | 6 +- + fs/netfs/buffered_write.c | 2 +- + fs/netfs/direct_write.c | 6 +- + fs/netfs/misc.c | 32 +++-- + fs/netfs/write_collect.c | 9 +- + fs/smb/client/cifsfs.c | 38 +++-- + fs/smb/client/cifssmb.c | 3 +- + fs/smb/client/file.c | 13 +- + fs/smb/client/inode.c | 14 +- + fs/smb/client/readdir.c | 3 +- + fs/smb/client/smb2ops.c | 42 +++--- + fs/smb/client/smb2pdu.c | 3 +- + include/linux/netfs.h | 293 ++++++++++++++++++++++++++++++++++++-- + 20 files changed, 450 insertions(+), 113 deletions(-) + +diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h +index d3aefbec4de6e..34c115d7c2502 100644 +--- a/fs/9p/v9fs_vfs.h ++++ b/fs/9p/v9fs_vfs.h +@@ -75,17 +75,4 @@ static inline void v9fs_invalidate_inode_attr(struct inode *inode) + + int v9fs_open_to_dotl_flags(int flags); + +-static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size) +-{ +- /* +- * 32-bit need the lock, concurrent updates could break the +- * sequences and make i_size_read() loop forever. +- * 64-bit updates are atomic and can skip the locking. +- */ +- if (sizeof(i_size) > sizeof(long)) +- spin_lock(&inode->i_lock); +- i_size_write(inode, i_size); +- if (sizeof(i_size) > sizeof(long)) +- spin_unlock(&inode->i_lock); +-} + #endif +diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c +index 97abe65bf7c1f..a0a5aec8e5d53 100644 +--- a/fs/9p/vfs_inode.c ++++ b/fs/9p/vfs_inode.c +@@ -1141,11 +1141,13 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode, + mode |= inode->i_mode & ~S_IALLUGO; + inode->i_mode = mode; + +- v9inode->netfs.remote_i_size = stat->length; ++ spin_lock(&inode->i_lock); ++ netfs_write_remote_i_size(inode, stat->length); + if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) +- v9fs_i_size_write(inode, stat->length); ++ i_size_write(inode, stat->length); + /* not real number of blocks, but 512 byte ones ... */ + inode->i_blocks = (stat->length + 512 - 1) >> 9; ++ spin_unlock(&inode->i_lock); + v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR; + } + +diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c +index 643e759eacb2a..d800f4fad555c 100644 +--- a/fs/9p/vfs_inode_dotl.c ++++ b/fs/9p/vfs_inode_dotl.c +@@ -634,10 +634,12 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode, + mode |= inode->i_mode & ~S_IALLUGO; + inode->i_mode = mode; + +- v9inode->netfs.remote_i_size = stat->st_size; ++ spin_lock(&inode->i_lock); ++ netfs_write_remote_i_size(inode, stat->st_size); + if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) +- v9fs_i_size_write(inode, stat->st_size); ++ i_size_write(inode, stat->st_size); + inode->i_blocks = stat->st_blocks; ++ spin_unlock(&inode->i_lock); + } else { + if (stat->st_result_mask & P9_STATS_ATIME) { + inode_set_atime(inode, stat->st_atime_sec, +@@ -662,13 +664,15 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode, + mode |= inode->i_mode & ~S_IALLUGO; + inode->i_mode = mode; + } ++ spin_lock(&inode->i_lock); + if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) && + stat->st_result_mask & P9_STATS_SIZE) { +- v9inode->netfs.remote_i_size = stat->st_size; +- v9fs_i_size_write(inode, stat->st_size); ++ netfs_write_remote_i_size(inode, stat->st_size); ++ i_size_write(inode, stat->st_size); + } + if (stat->st_result_mask & P9_STATS_BLOCKS) + inode->i_blocks = stat->st_blocks; ++ spin_unlock(&inode->i_lock); + } + if (stat->st_result_mask & P9_STATS_GEN) + inode->i_generation = stat->st_gen; +diff --git a/fs/afs/file.c b/fs/afs/file.c +index 74d04af51ff4a..650595e1c3f37 100644 +--- a/fs/afs/file.c ++++ b/fs/afs/file.c +@@ -424,21 +424,35 @@ static void afs_free_request(struct netfs_io_request *rreq) + afs_put_wb_key(rreq->netfs_priv2); + } + +-static void afs_update_i_size(struct inode *inode, loff_t new_i_size) ++/* ++ * Set the file size and block count, taking ->cb_lock and ->i_lock to maintain ++ * coherency and prevent 64-bit tearing on 32-bit arches. ++ * ++ * Also, estimate the number of 512 bytes blocks used, rounded up to nearest 1K ++ * for consistency with other AFS clients. ++ */ ++void afs_set_i_size(struct afs_vnode *vnode, loff_t new_i_size) + { +- struct afs_vnode *vnode = AFS_FS_I(inode); ++ struct inode *inode = &vnode->netfs.inode; + loff_t i_size; + + write_seqlock(&vnode->cb_lock); +- i_size = i_size_read(&vnode->netfs.inode); ++ spin_lock(&inode->i_lock); ++ i_size = i_size_read(inode); + if (new_i_size > i_size) { +- i_size_write(&vnode->netfs.inode, new_i_size); +- inode_set_bytes(&vnode->netfs.inode, new_i_size); ++ i_size_write(inode, new_i_size); ++ inode_set_bytes(inode, round_up(new_i_size, 1024)); + } ++ spin_unlock(&inode->i_lock); + write_sequnlock(&vnode->cb_lock); + fscache_update_cookie(afs_vnode_cache(vnode), NULL, &new_i_size); + } + ++static void afs_update_i_size(struct inode *inode, loff_t new_i_size) ++{ ++ afs_set_i_size(AFS_FS_I(inode), new_i_size); ++} ++ + static void afs_netfs_invalidate_cache(struct netfs_io_request *wreq) + { + struct afs_vnode *vnode = AFS_FS_I(wreq->inode); +diff --git a/fs/afs/inode.c b/fs/afs/inode.c +index dde1857fcabb3..df95b39ed308e 100644 +--- a/fs/afs/inode.c ++++ b/fs/afs/inode.c +@@ -224,7 +224,8 @@ static int afs_inode_init_from_status(struct afs_operation *op, + return afs_protocol_error(NULL, afs_eproto_file_type); + } + +- afs_set_i_size(vnode, status->size); ++ i_size_write(inode, status->size); ++ inode_set_bytes(inode, status->size); + afs_set_netfs_context(vnode); + + vnode->invalid_before = status->data_version; +@@ -253,7 +254,8 @@ static void afs_apply_status(struct afs_operation *op, + { + struct afs_file_status *status = &vp->scb.status; + struct afs_vnode *vnode = vp->vnode; +- struct inode *inode = &vnode->netfs.inode; ++ struct netfs_inode *ictx = &vnode->netfs; ++ struct inode *inode = &ictx->inode; + struct timespec64 t; + umode_t mode; + bool unexpected_jump = false; +@@ -336,6 +338,8 @@ static void afs_apply_status(struct afs_operation *op, + } + + if (data_changed) { ++ unsigned long long zero_point, size = status->size; ++ + inode_set_iversion_raw(inode, status->data_version); + + /* Only update the size if the data version jumped. If the +@@ -343,16 +347,25 @@ static void afs_apply_status(struct afs_operation *op, + * idea of what the size should be that's not the same as + * what's on the server. + */ +- vnode->netfs.remote_i_size = status->size; +- if (change_size || status->size > i_size_read(inode)) { +- afs_set_i_size(vnode, status->size); ++ spin_lock(&inode->i_lock); ++ ++ if (change_size || size > i_size_read(inode)) { ++ /* We can read the sizes directly as we hold i_lock. */ ++ zero_point = ictx->_zero_point; ++ + if (unexpected_jump) +- vnode->netfs.zero_point = status->size; ++ zero_point = size; ++ netfs_write_sizes(inode, size, size, zero_point); ++ inode_set_bytes(inode, size); + inode_set_ctime_to_ts(inode, t); + inode_set_atime_to_ts(inode, t); ++ } else { ++ netfs_write_remote_i_size(inode, size); + } ++ spin_unlock(&inode->i_lock); ++ + if (op->ops == &afs_fetch_data_operation) +- op->fetch.subreq->rreq->i_size = status->size; ++ op->fetch.subreq->rreq->i_size = size; + } + } + +@@ -709,7 +722,7 @@ int afs_getattr(struct mnt_idmap *idmap, const struct path *path, + * it, but we need to give userspace the server's size. + */ + if (S_ISDIR(inode->i_mode)) +- stat->size = vnode->netfs.remote_i_size; ++ stat->size = netfs_read_remote_i_size(inode); + } while (read_seqretry(&vnode->cb_lock, seq)); + + return 0; +@@ -889,7 +902,7 @@ int afs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + */ + if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) && + attr->ia_size < i_size && +- attr->ia_size > vnode->netfs.remote_i_size) { ++ attr->ia_size > netfs_read_remote_i_size(inode)) { + truncate_setsize(inode, attr->ia_size); + netfs_resize_file(&vnode->netfs, size, false); + fscache_resize_cookie(afs_vnode_cache(vnode), +diff --git a/fs/afs/internal.h b/fs/afs/internal.h +index 009064b8d6616..fb0449d024ff2 100644 +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -1158,6 +1158,7 @@ extern int afs_open(struct inode *, struct file *); + extern int afs_release(struct inode *, struct file *); + void afs_fetch_data_async_rx(struct work_struct *work); + void afs_fetch_data_immediate_cancel(struct afs_call *call); ++void afs_set_i_size(struct afs_vnode *vnode, loff_t new_i_size); + + /* + * flock.c +@@ -1759,16 +1760,6 @@ static inline void afs_update_dentry_version(struct afs_operation *op, + (void *)(unsigned long)dir_vp->scb.status.data_version; + } + +-/* +- * Set the file size and block count. Estimate the number of 512 bytes blocks +- * used, rounded up to nearest 1K for consistency with other AFS clients. +- */ +-static inline void afs_set_i_size(struct afs_vnode *vnode, u64 size) +-{ +- i_size_write(&vnode->netfs.inode, size); +- vnode->netfs.inode.i_blocks = ((size + 1023) >> 10) << 1; +-} +- + /* + * Check for a conflicting operation on a directory that we just unlinked from. + * If someone managed to sneak a link or an unlink in on the file we just +diff --git a/fs/afs/write.c b/fs/afs/write.c +index 93ad86ff33453..e2ef19a73bbfc 100644 +--- a/fs/afs/write.c ++++ b/fs/afs/write.c +@@ -143,7 +143,7 @@ static void afs_issue_write_worker(struct work_struct *work) + afs_begin_vnode_operation(op); + + op->store.write_iter = &subreq->io_iter; +- op->store.i_size = umax(pos + len, vnode->netfs.remote_i_size); ++ op->store.i_size = umax(pos + len, netfs_read_remote_i_size(&vnode->netfs.inode)); + op->mtime = inode_get_mtime(&vnode->netfs.inode); + + afs_wait_for_operation(op); +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index fee0aebf5a3d6..ebd84a6cc3f09 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -209,7 +209,6 @@ static void netfs_issue_read(struct netfs_io_request *rreq, + static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + struct readahead_control *ractl) + { +- struct netfs_inode *ictx = netfs_inode(rreq->inode); + unsigned long long start = rreq->start; + ssize_t size = rreq->len; + int ret = 0; +@@ -233,7 +232,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size); + subreq->source = source; + if (source == NETFS_DOWNLOAD_FROM_SERVER) { +- unsigned long long zp = umin(ictx->zero_point, rreq->i_size); ++ unsigned long long zero_point = netfs_read_zero_point(rreq->inode); ++ unsigned long long zp = umin(zero_point, rreq->i_size); + size_t len = subreq->len; + + if (unlikely(rreq->origin == NETFS_READ_SINGLE)) +@@ -249,7 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, + pr_err("ZERO-LEN READ: R=%08x[%x] l=%zx/%zx s=%llx z=%llx i=%llx", + rreq->debug_id, subreq->debug_index, + subreq->len, size, +- subreq->start, ictx->zero_point, rreq->i_size); ++ subreq->start, zero_point, rreq->i_size); + netfs_cancel_read(subreq, ret); + break; + } +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 22a4d61631c9d..c887a30c14d91 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -231,7 +231,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + * server would just return a block of zeros or a short read if + * we try to read it. + */ +- if (fpos >= ctx->zero_point) { ++ if (fpos >= netfs_read_zero_point(inode)) { + folio_zero_segment(folio, 0, offset); + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); + if (unlikely(copied == 0)) +diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c +index f9ab69de3e298..25f8ceb15fad6 100644 +--- a/fs/netfs/direct_write.c ++++ b/fs/netfs/direct_write.c +@@ -376,8 +376,10 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from) + if (ret < 0) + goto out; + end = iocb->ki_pos + iov_iter_count(from); +- if (end > ictx->zero_point) +- ictx->zero_point = end; ++ spin_lock(&inode->i_lock); ++ if (end > ictx->_zero_point) ++ netfs_write_zero_point(inode, end); ++ spin_unlock(&inode->i_lock); + + fscache_invalidate(netfs_i_cookie(ictx), NULL, i_size_read(inode), + FSCACHE_INVAL_DIO_WRITE); +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index 21357907b7eee..bad661ff2bec8 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -211,18 +211,25 @@ EXPORT_SYMBOL(netfs_clear_inode_writeback); + void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) + { + struct netfs_folio *finfo; +- struct netfs_inode *ctx = netfs_inode(folio_inode(folio)); ++ struct inode *inode = folio_inode(folio); ++ struct netfs_inode *ctx = netfs_inode(inode); + size_t flen = folio_size(folio); + + _enter("{%lx},%zx,%zx", folio->index, offset, length); + + if (offset == 0 && length == flen) { +- unsigned long long i_size = i_size_read(&ctx->inode); ++ unsigned long long i_size, remote_i_size, zero_point; + unsigned long long fpos = folio_pos(folio), end; + ++ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); + end = umin(fpos + flen, i_size); +- if (fpos < i_size && end > ctx->zero_point) +- ctx->zero_point = end; ++ if (fpos < i_size && end > zero_point) { ++ spin_lock(&inode->i_lock); ++ end = umin(fpos + flen, inode->i_size); ++ if (fpos < i_size && end > ctx->_zero_point) ++ netfs_write_zero_point(inode, end); ++ spin_unlock(&inode->i_lock); ++ } + } + + folio_wait_private_2(folio); /* [DEPRECATED] */ +@@ -292,15 +299,22 @@ EXPORT_SYMBOL(netfs_invalidate_folio); + */ + bool netfs_release_folio(struct folio *folio, gfp_t gfp) + { +- struct netfs_inode *ctx = netfs_inode(folio_inode(folio)); +- unsigned long long end; ++ struct inode *inode = folio_inode(folio); ++ struct netfs_inode *ctx = netfs_inode(inode); ++ unsigned long long i_size, remote_i_size, zero_point, end; + + if (folio_test_dirty(folio)) + return false; + +- end = umin(folio_next_pos(folio), i_size_read(&ctx->inode)); +- if (end > ctx->zero_point) +- ctx->zero_point = end; ++ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); ++ end = umin(folio_next_pos(folio), i_size); ++ if (end > zero_point) { ++ spin_lock(&inode->i_lock); ++ end = umin(folio_next_pos(folio), inode->i_size); ++ if (end > ctx->_zero_point) ++ netfs_write_zero_point(inode, end); ++ spin_unlock(&inode->i_lock); ++ } + + if (folio_test_private(folio)) + return false; +diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c +index 7fbf50907a7fc..24fc2bb2f8a47 100644 +--- a/fs/netfs/write_collect.c ++++ b/fs/netfs/write_collect.c +@@ -57,7 +57,8 @@ static void netfs_dump_request(const struct netfs_io_request *rreq) + int netfs_folio_written_back(struct folio *folio) + { + enum netfs_folio_trace why = netfs_folio_trace_clear; +- struct netfs_inode *ictx = netfs_inode(folio->mapping->host); ++ struct inode *inode = folio_inode(folio); ++ struct netfs_inode *ictx = netfs_inode(inode); + struct netfs_folio *finfo; + struct netfs_group *group = NULL; + int gcount = 0; +@@ -69,8 +70,10 @@ int netfs_folio_written_back(struct folio *folio) + unsigned long long fend; + + fend = folio_pos(folio) + finfo->dirty_offset + finfo->dirty_len; +- if (fend > ictx->zero_point) +- ictx->zero_point = fend; ++ spin_lock(&ictx->inode.i_lock); ++ if (fend > ictx->_zero_point) ++ netfs_write_zero_point(inode, fend); ++ spin_unlock(&ictx->inode.i_lock); + + folio_detach_private(folio); + group = finfo->netfs_group; +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 97931e1ae3b2b..db6062dcbb3ec 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -470,7 +470,8 @@ cifs_alloc_inode(struct super_block *sb) + spin_lock_init(&cifs_inode->writers_lock); + cifs_inode->writers = 0; + cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ +- cifs_inode->netfs.remote_i_size = 0; ++ cifs_inode->netfs._remote_i_size = 0; ++ cifs_inode->netfs._zero_point = 0; + cifs_inode->uniqueid = 0; + cifs_inode->createtime = 0; + cifs_inode->epoch = 0; +@@ -1338,7 +1339,8 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + struct cifsFileInfo *smb_file_src = src_file->private_data; + struct cifsFileInfo *smb_file_target = dst_file->private_data; + struct cifs_tcon *target_tcon, *src_tcon; +- unsigned long long destend, fstart, fend, old_size, new_size; ++ unsigned long long i_size, old_size, new_size, zero_point; ++ unsigned long long destend, fstart, fend; + unsigned int xid; + int rc; + +@@ -1382,7 +1384,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + * Advance the EOF marker after the flush above to the end of the range + * if it's short of that. + */ +- if (src_cifsi->netfs.remote_i_size < off + len) { ++ if (netfs_read_remote_i_size(src_inode) < off + len) { + rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len); + if (rc < 0) + goto unlock; +@@ -1403,16 +1405,18 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + rc = cifs_flush_folio(target_inode, destend, &fstart, &fend, false); + if (rc) + goto unlock; +- if (fend > target_cifsi->netfs.zero_point) +- target_cifsi->netfs.zero_point = fend + 1; +- old_size = target_cifsi->netfs.remote_i_size; ++ ++ spin_lock(&target_inode->i_lock); ++ if (fend > zero_point) ++ netfs_write_zero_point(target_inode, fend + 1); ++ i_size = target_inode->i_size; ++ spin_unlock(&target_inode->i_lock); + + /* Discard all the folios that overlap the destination region. */ + cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend); + truncate_inode_pages_range(&target_inode->i_data, fstart, fend); + +- fscache_invalidate(cifs_inode_cookie(target_inode), NULL, +- i_size_read(target_inode), 0); ++ fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0); + + rc = -EOPNOTSUPP; + if (target_tcon->ses->server->ops->duplicate_extents) { +@@ -1437,8 +1441,12 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + rc = -EINVAL; + } + } +- if (rc == 0 && new_size > target_cifsi->netfs.zero_point) +- target_cifsi->netfs.zero_point = new_size; ++ if (rc == 0) { ++ spin_lock(&target_inode->i_lock); ++ if (new_size > target_cifsi->netfs._zero_point) ++ netfs_write_zero_point(target_inode, new_size); ++ spin_unlock(&target_inode->i_lock); ++ } + } + + /* force revalidate of size and timestamps of target file now +@@ -1509,7 +1517,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, + * Advance the EOF marker after the flush above to the end of the range + * if it's short of that. + */ +- if (src_cifsi->netfs.remote_i_size < off + len) { ++ if (netfs_read_remote_i_size(src_inode) < off + len) { + rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len); + if (rc < 0) + goto unlock; +@@ -1537,8 +1545,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, + fscache_resize_cookie(cifs_inode_cookie(target_inode), + i_size_read(target_inode)); + } +- if (rc > 0 && destoff + rc > target_cifsi->netfs.zero_point) +- target_cifsi->netfs.zero_point = destoff + rc; ++ if (rc > 0) { ++ spin_lock(&target_inode->i_lock); ++ if (destoff + rc > target_cifsi->netfs._zero_point) ++ netfs_write_zero_point(target_inode, destoff + rc); ++ spin_unlock(&target_inode->i_lock); ++ } + } + + file_accessed(src_file); +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 3990a90122640..9e27bfa7376b1 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1465,6 +1465,7 @@ cifs_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) + struct cifs_io_subrequest *rdata = mid->callback_data; + struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode); + struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); ++ struct inode *inode = &ictx->inode; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 1, + .rq_iter = rdata->subreq.io_iter }; +@@ -1538,7 +1539,7 @@ cifs_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) + } else { + size_t trans = rdata->subreq.transferred + rdata->got_bytes; + if (trans < rdata->subreq.len && +- rdata->subreq.start + trans >= ictx->remote_i_size) { ++ rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) { + rdata->result = 0; + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); + } else if (rdata->got_bytes > 0) { +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index a69e05f86d7e2..ad624c01193eb 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -2491,18 +2491,23 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) + void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result) + { + struct netfs_io_request *wreq = wdata->rreq; +- struct netfs_inode *ictx = netfs_inode(wreq->inode); ++ struct inode *inode = wreq->inode; ++ struct netfs_inode *ictx = netfs_inode(inode); + loff_t wrend; + + if (result > 0) { ++ spin_lock(&inode->i_lock); ++ + wrend = wdata->subreq.start + wdata->subreq.transferred + result; + +- if (wrend > ictx->zero_point && ++ if (wrend > ictx->_zero_point && + (wdata->rreq->origin == NETFS_UNBUFFERED_WRITE || + wdata->rreq->origin == NETFS_DIO_WRITE)) +- ictx->zero_point = wrend; +- if (wrend > ictx->remote_i_size) ++ netfs_write_zero_point(inode, wrend); ++ if (wrend > ictx->_remote_i_size) + netfs_resize_file(ictx, wrend, true); ++ ++ spin_unlock(&inode->i_lock); + } + + netfs_write_subrequest_terminated(&wdata->subreq, result); +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 888f9e35f14b8..5b1beba77c0ec 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -119,7 +119,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + mtime = inode_get_mtime(inode); + if (timespec64_equal(&mtime, &fattr->cf_mtime) && +- cifs_i->netfs.remote_i_size == fattr->cf_eof) { ++ netfs_read_remote_i_size(inode) == fattr->cf_eof) { + cifs_dbg(FYI, "%s: inode %llu is unchanged\n", + __func__, cifs_i->uniqueid); + return; +@@ -173,12 +173,12 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, + CIFS_I(inode)->time = 0; /* force reval */ + return -ESTALE; + } +- if (inode_state_read_once(inode) & I_NEW) +- CIFS_I(inode)->netfs.zero_point = fattr->cf_eof; +- + cifs_revalidate_cache(inode, fattr); + + spin_lock(&inode->i_lock); ++ if (inode_state_read_once(inode) & I_NEW) ++ netfs_write_zero_point(inode, fattr->cf_eof); ++ + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); + fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); +@@ -212,7 +212,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, + else + clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); + +- cifs_i->netfs.remote_i_size = fattr->cf_eof; ++ netfs_write_remote_i_size(inode, fattr->cf_eof); + /* + * Can't safely change the file size here if the client is writing to + * it due to potential races. +@@ -2771,7 +2771,9 @@ cifs_revalidate_mapping(struct inode *inode) + if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_RW_CACHE) + goto skip_invalidate; + +- cifs_inode->netfs.zero_point = cifs_inode->netfs.remote_i_size; ++ spin_lock(&inode->i_lock); ++ netfs_write_zero_point(inode, netfs_inode(inode)->_remote_i_size); ++ spin_unlock(&inode->i_lock); + rc = filemap_invalidate_inode(inode, true, 0, LLONG_MAX); + if (rc) { + cifs_dbg(VFS, "%s: invalidate inode %p failed with rc %d\n", +diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c +index be22bbc4a65a0..e860fa08b5e30 100644 +--- a/fs/smb/client/readdir.c ++++ b/fs/smb/client/readdir.c +@@ -143,7 +143,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, + fattr->cf_rdev = inode->i_rdev; + fattr->cf_uid = inode->i_uid; + fattr->cf_gid = inode->i_gid; +- fattr->cf_eof = CIFS_I(inode)->netfs.remote_i_size; ++ fattr->cf_eof = ++ netfs_read_remote_i_size(inode); + fattr->cf_symlink_target = NULL; + } else { + CIFS_I(inode)->time = 0; +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 00735607ce953..a07d72cd16dc1 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -3402,8 +3402,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + struct inode *inode = file_inode(file); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsFileInfo *cfile = file->private_data; +- struct netfs_inode *ictx = netfs_inode(inode); +- unsigned long long i_size, new_size, remote_size; ++ unsigned long long i_size, new_size, remote_i_size, zero_point; + long rc; + unsigned int xid; + +@@ -3414,9 +3413,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + + filemap_invalidate_lock(inode->i_mapping); + +- i_size = i_size_read(inode); +- remote_size = ictx->remote_i_size; +- if (offset + len >= remote_size && offset < i_size) { ++ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); ++ if (offset + len >= remote_i_size && offset < i_size) { + unsigned long long top = umin(offset + len, i_size); + + rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1); +@@ -3449,9 +3447,11 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + cfile->fid.volatile_fid, cfile->pid, new_size); + if (rc >= 0) { + truncate_setsize(inode, new_size); ++ spin_lock(&inode->i_lock); + netfs_resize_file(&cifsi->netfs, new_size, true); +- if (offset < cifsi->netfs.zero_point) +- cifsi->netfs.zero_point = offset; ++ if (offset < cifsi->netfs._zero_point) ++ netfs_write_zero_point(inode, offset); ++ spin_unlock(&inode->i_lock); + fscache_resize_cookie(cifs_inode_cookie(inode), new_size); + } + } +@@ -3474,7 +3474,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; +- unsigned long long end = offset + len, i_size, remote_i_size; ++ unsigned long long end = offset + len, i_size, remote_i_size, zero_point; + long rc; + unsigned int xid; + __u8 set_sparse = 1; +@@ -3516,14 +3516,17 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + * that we locally hole-punch the tail of the dirty data, the proposed + * EOF update will end up in the wrong place. + */ +- i_size = i_size_read(inode); +- remote_i_size = netfs_inode(inode)->remote_i_size; ++ netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); ++ + if (end > remote_i_size && i_size > remote_i_size) { + unsigned long long extend_to = umin(end, i_size); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, extend_to); +- if (rc >= 0) +- netfs_inode(inode)->remote_i_size = extend_to; ++ if (rc >= 0) { ++ spin_lock(&inode->i_lock); ++ netfs_write_remote_i_size(inode, extend_to); ++ spin_unlock(&inode->i_lock); ++ } + } + + unlock: +@@ -3787,7 +3790,6 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + struct inode *inode = file_inode(file); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsFileInfo *cfile = file->private_data; +- struct netfs_inode *ictx = &cifsi->netfs; + loff_t old_eof, new_eof; + + xid = get_xid(); +@@ -3805,7 +3807,9 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + goto out_2; + + truncate_pagecache_range(inode, off, old_eof); +- ictx->zero_point = old_eof; ++ spin_lock(&inode->i_lock); ++ netfs_write_zero_point(inode, old_eof); ++ spin_unlock(&inode->i_lock); + netfs_wait_for_outstanding_io(inode); + + rc = smb2_copychunk_range(xid, cfile, cfile, off + len, +@@ -3822,8 +3826,10 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + rc = 0; + + truncate_setsize(inode, new_eof); ++ spin_lock(&inode->i_lock); + netfs_resize_file(&cifsi->netfs, new_eof, true); +- ictx->zero_point = new_eof; ++ netfs_write_zero_point(inode, new_eof); ++ spin_unlock(&inode->i_lock); + fscache_resize_cookie(cifs_inode_cookie(inode), new_eof); + out_2: + filemap_invalidate_unlock(inode->i_mapping); +@@ -3866,13 +3872,17 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, + goto out_2; + + truncate_setsize(inode, new_eof); ++ spin_lock(&inode->i_lock); + netfs_resize_file(&cifsi->netfs, i_size_read(inode), true); ++ spin_unlock(&inode->i_lock); + fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode)); + + rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); + if (rc < 0) + goto out_2; +- cifsi->netfs.zero_point = new_eof; ++ spin_lock(&inode->i_lock); ++ netfs_write_zero_point(inode, new_eof); ++ spin_unlock(&inode->i_lock); + + rc = smb3_zero_data(file, tcon, off, len, xid); + if (rc < 0) +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 5188218c25be4..967047894a1e6 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -4596,6 +4596,7 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) + struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode); + struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); + struct smb2_hdr *shdr = (struct smb2_hdr *)rdata->iov[0].iov_base; ++ struct inode *inode = &ictx->inode; + struct cifs_credits credits = { + .value = 0, + .instance = 0, +@@ -4709,7 +4710,7 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) + } else { + size_t trans = rdata->subreq.transferred + rdata->got_bytes; + if (trans < rdata->subreq.len && +- rdata->subreq.start + trans >= ictx->remote_i_size) { ++ rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) { + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); + rdata->result = 0; + } +diff --git a/include/linux/netfs.h b/include/linux/netfs.h +index ba17ac5bf356a..4fd1d796ad73b 100644 +--- a/include/linux/netfs.h ++++ b/include/linux/netfs.h +@@ -62,8 +62,8 @@ struct netfs_inode { + struct fscache_cookie *cache; + #endif + struct mutex wb_lock; /* Writeback serialisation */ +- loff_t remote_i_size; /* Size of the remote file */ +- loff_t zero_point; /* Size after which we assume there's no data ++ loff_t _remote_i_size; /* Size of the remote file */ ++ loff_t _zero_point; /* Size after which we assume there's no data + * on the server */ + atomic_t io_count; /* Number of outstanding reqs */ + unsigned long flags; +@@ -474,6 +474,254 @@ static inline struct netfs_inode *netfs_inode(struct inode *inode) + return container_of(inode, struct netfs_inode, inode); + } + ++/** ++ * netfs_read_remote_i_size - Read remote_i_size safely ++ * @inode: The inode to access ++ * ++ * Read remote_i_size safely without the potential for tearing on 32-bit ++ * arches. ++ * ++ * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the ++ * i_size_read/write must be atomic with respect to the local cpu (unlike with ++ * preempt disabled), but they don't need to be atomic with respect to other ++ * cpus like in true SMP (so they need either to either locally disable irq ++ * around the read or for example on x86 they can be still implemented as a ++ * cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit ++ * archs it makes no difference if preempt is enabled or not. ++ */ ++static inline unsigned long long netfs_read_remote_i_size(const struct inode *inode) ++{ ++ const struct netfs_inode *ictx = container_of(inode, struct netfs_inode, inode); ++ unsigned long long remote_i_size; ++ ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ unsigned int seq; ++ ++ do { ++ seq = read_seqcount_begin(&inode->i_size_seqcount); ++ remote_i_size = ictx->_remote_i_size; ++ } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ remote_i_size = ictx->_remote_i_size; ++ preempt_enable(); ++#else ++ /* Pairs with smp_store_release() in netfs_write_remote_i_size() */ ++ remote_i_size = smp_load_acquire(&ictx->_remote_i_size); ++#endif ++ return remote_i_size; ++} ++ ++/* ++ * netfs_write_remote_i_size - Set remote_i_size safely ++ * @inode: The inode to access ++ * @remote_i_size: The new value for the size of the file on the server ++ * ++ * Set remote_i_size safely without the potential for tearing on 32-bit arches. ++ * ++ * Context: The caller must hold inode->i_lock. ++ * ++ * NOTE: unlike netfs_read_remote_i_size(), netfs_write_remote_i_size() does ++ * need locking around it (normally i_rwsem), otherwise on 32bit/SMP an update ++ * of i_size_seqcount can be lost, resulting in subsequent i_size_read() calls ++ * spinning forever. ++ */ ++static inline void netfs_write_remote_i_size(struct inode *inode, ++ unsigned long long remote_i_size) ++{ ++ struct netfs_inode *ictx = netfs_inode(inode); ++ ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ write_seqcount_begin(&inode->i_size_seqcount); ++ ictx->_remote_i_size = remote_i_size; ++ write_seqcount_end(&inode->i_size_seqcount); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ ictx->_remote_i_size = remote_i_size; ++ preempt_enable(); ++#else ++ /* ++ * Pairs with smp_load_acquire() in netfs_read_remote_i_size() to ++ * ensure changes related to inode size (such as page contents) are ++ * visible before we see the changed inode size. ++ */ ++ smp_store_release(&ictx->_remote_i_size, remote_i_size); ++#endif ++} ++ ++/** ++ * netfs_read_zero_point - Read zero_point safely ++ * @inode: The inode to access ++ * ++ * Read zero_point safely without the potential for tearing on 32-bit ++ * arches. ++ * ++ * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the ++ * i_size_read/write must be atomic with respect to the local cpu (unlike with ++ * preempt disabled), but they don't need to be atomic with respect to other ++ * cpus like in true SMP (so they need either to either locally disable irq ++ * around the read or for example on x86 they can be still implemented as a ++ * cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit ++ * archs it makes no difference if preempt is enabled or not. ++ */ ++static inline unsigned long long netfs_read_zero_point(const struct inode *inode) ++{ ++ struct netfs_inode *ictx = container_of(inode, struct netfs_inode, inode); ++ unsigned long long zero_point; ++ ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ unsigned int seq; ++ ++ do { ++ seq = read_seqcount_begin(&inode->i_size_seqcount); ++ zero_point = ictx->_zero_point; ++ } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ zero_point = ictx->_zero_point; ++ preempt_enable(); ++#else ++ /* Pairs with smp_store_release() in netfs_write_zero_point() */ ++ zero_point = smp_load_acquire(&ictx->_zero_point); ++#endif ++ return zero_point; ++} ++ ++/* ++ * netfs_write_zero_point - Set zero_point safely ++ * @inode: The inode to access ++ * @zero_point: The new value for the point beyond which the server has no data ++ * ++ * Set zero_point safely without the potential for tearing on 32-bit arches. ++ * ++ * Context: The caller must hold inode->i_lock. ++ * ++ * NOTE: unlike netfs_read_zero_point(), netfs_write_zero_point() does need ++ * locking around it (normally i_rwsem), otherwise on 32bit/SMP an update of ++ * i_size_seqcount can be lost, resulting in subsequent read calls spinning ++ * forever. ++ */ ++static inline void netfs_write_zero_point(struct inode *inode, ++ unsigned long long zero_point) ++{ ++ struct netfs_inode *ictx = netfs_inode(inode); ++ ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ write_seqcount_begin(&inode->i_size_seqcount); ++ ictx->_zero_point = zero_point; ++ write_seqcount_end(&inode->i_size_seqcount); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ ictx->_zero_point = zero_point; ++ preempt_enable(); ++#else ++ /* ++ * Pairs with smp_load_acquire() in netfs_read_zero_point() to ++ * ensure changes related to inode size (such as page contents) are ++ * visible before we see the changed inode size. ++ */ ++ smp_store_release(&ictx->_zero_point, zero_point); ++#endif ++} ++ ++/** ++ * netfs_read_sizes - Read remote_i_size and zero_point safely ++ * @inode: The inode to access ++ * @i_size: Where to return the local file size. ++ * @remote_i_size: Where to return the size of the file on the server ++ * @zero_point: Where to return the the point beyond which the server has no data ++ * ++ * Read remote_i_size and zero_point safely without the potential for tearing ++ * on 32-bit arches. ++ * ++ * NOTE: in a 32bit arch with a preemptable kernel and an UP compile the ++ * i_size_read/write must be atomic with respect to the local cpu (unlike with ++ * preempt disabled), but they don't need to be atomic with respect to other ++ * cpus like in true SMP (so they need either to either locally disable irq ++ * around the read or for example on x86 they can be still implemented as a ++ * cmpxchg8b without the need of the lock prefix). For SMP compiles and 64bit ++ * archs it makes no difference if preempt is enabled or not. ++ */ ++static inline void netfs_read_sizes(const struct inode *inode, ++ unsigned long long *i_size, ++ unsigned long long *remote_i_size, ++ unsigned long long *zero_point) ++{ ++ const struct netfs_inode *ictx = container_of(inode, struct netfs_inode, inode); ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ unsigned int seq; ++ ++ do { ++ seq = read_seqcount_begin(&inode->i_size_seqcount); ++ *i_size = inode->i_size; ++ *remote_i_size = ictx->_remote_i_size; ++ *zero_point = ictx->_zero_point; ++ } while (read_seqcount_retry(&inode->i_size_seqcount, seq)); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ *i_size = inode->i_size; ++ *remote_i_size = ictx->_remote_i_size; ++ *zero_point = ictx->_zero_point; ++ preempt_enable(); ++#else ++ /* Pairs with smp_store_release() in i_size_write() */ ++ *i_size = smp_load_acquire(&inode->i_size); ++ /* Pairs with smp_store_release() in netfs_write_remote_i_size() */ ++ *remote_i_size = smp_load_acquire(&ictx->_remote_i_size); ++ /* Pairs with smp_store_release() in netfs_write_zero_point() */ ++ *zero_point = smp_load_acquire(&ictx->_zero_point); ++#endif ++} ++ ++/* ++ * netfs_write_sizes - Set i_size, remote_i_size and zero_point safely ++ * @inode: The inode to access ++ * @i_size: The new value for the local size of the file ++ * @remote_i_size: The new value for the size of the file on the server ++ * @zero_point: The new value for the point beyond which the server has no data ++ * ++ * Set both remote_i_size and zero_point safely without the potential for ++ * tearing on 32-bit arches. ++ * ++ * Context: The caller must hold inode->i_lock. ++ * ++ * NOTE: unlike netfs_read_zero_point(), netfs_write_zero_point() does need ++ * locking around it (normally i_rwsem), otherwise on 32bit/SMP an update of ++ * i_size_seqcount can be lost, resulting in subsequent read calls spinning ++ * forever. ++ */ ++static inline void netfs_write_sizes(struct inode *inode, ++ unsigned long long i_size, ++ unsigned long long remote_i_size, ++ unsigned long long zero_point) ++{ ++ struct netfs_inode *ictx = netfs_inode(inode); ++ ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ write_seqcount_begin(&inode->i_size_seqcount); ++ inode->i_size = i_size; ++ ictx->_remote_i_size = remote_i_size; ++ ictx->_zero_point = zero_point; ++ write_seqcount_end(&inode->i_size_seqcount); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); ++ inode->i_size = i_size; ++ ictx->_remote_i_size = remote_i_size; ++ ictx->_zero_point = zero_point; ++ preempt_enable(); ++#else ++ /* ++ * Pairs with smp_load_acquire() in i_size_read(), ++ * netfs_read_remote_i_size() and netfs_read_zero_point() to ensure ++ * changes related to inode size (such as page contents) are visible ++ * before we see the changed inode size. ++ */ ++ smp_store_release(&inode->i_size, i_size); ++ smp_store_release(&ictx->_remote_i_size, remote_i_size); ++ smp_store_release(&ictx->_zero_point, zero_point); ++#endif ++} ++ + /** + * netfs_inode_init - Initialise a netfslib inode context + * @ctx: The netfs inode to initialise +@@ -488,8 +736,8 @@ static inline void netfs_inode_init(struct netfs_inode *ctx, + bool use_zero_point) + { + ctx->ops = ops; +- ctx->remote_i_size = i_size_read(&ctx->inode); +- ctx->zero_point = LLONG_MAX; ++ ctx->_remote_i_size = i_size_read(&ctx->inode); ++ ctx->_zero_point = LLONG_MAX; + ctx->flags = 0; + atomic_set(&ctx->io_count, 0); + #if IS_ENABLED(CONFIG_FSCACHE) +@@ -498,7 +746,7 @@ static inline void netfs_inode_init(struct netfs_inode *ctx, + mutex_init(&ctx->wb_lock); + /* ->releasepage() drives zero_point */ + if (use_zero_point) { +- ctx->zero_point = ctx->remote_i_size; ++ ctx->_zero_point = ctx->_remote_i_size; + mapping_set_release_always(ctx->inode.i_mapping); + } + } +@@ -511,13 +759,40 @@ static inline void netfs_inode_init(struct netfs_inode *ctx, + * + * Inform the netfs lib that a file got resized so that it can adjust its state. + */ +-static inline void netfs_resize_file(struct netfs_inode *ctx, loff_t new_i_size, ++static inline void netfs_resize_file(struct netfs_inode *ictx, ++ unsigned long long new_i_size, + bool changed_on_server) + { ++#if BITS_PER_LONG==32 && defined(CONFIG_SMP) ++ struct inode *inode = &ictx->inode; ++ ++ preempt_disable(); ++ write_seqcount_begin(&inode->i_size_seqcount); ++ if (changed_on_server) ++ ictx->_remote_i_size = new_i_size; ++ if (new_i_size < ictx->_zero_point) ++ ictx->_zero_point = new_i_size; ++ write_seqcount_end(&inode->i_size_seqcount); ++ preempt_enable(); ++#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION) ++ preempt_disable(); + if (changed_on_server) +- ctx->remote_i_size = new_i_size; +- if (new_i_size < ctx->zero_point) +- ctx->zero_point = new_i_size; ++ ictx->_remote_i_size = new_i_size; ++ if (new_i_size < ictx->_zero_point) ++ ictx->_zero_point = new_i_size; ++ preempt_enable(); ++#else ++ /* ++ * Pairs with smp_load_acquire() in netfs_read_remote_i_size and ++ * netfs_read_zero_point() to ensure changes related to inode size ++ * (such as page contents) are visible before we see the changed inode ++ * size. ++ */ ++ if (changed_on_server) ++ smp_store_release(&ictx->_remote_i_size, new_i_size); ++ if (new_i_size < ictx->_zero_point) ++ smp_store_release(&ictx->_zero_point, new_i_size); ++#endif + } + + /** +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch b/queue-7.0/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch new file mode 100644 index 0000000000..53b540d3e7 --- /dev/null +++ b/queue-7.0/netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch @@ -0,0 +1,104 @@ +From c16806a55a5d78f632fb02b6f265ba275223f481 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:56 +0100 +Subject: netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages() + +From: David Howells + +[ Upstream commit dbe556972100fabb8e5a1b3d2163831ff07b1e8e ] + +netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it +is wanting to unlock and compares that to rreq->no_unlock_folio so that it +doesn't unlock a folio being read for netfs_perform_write() or +netfs_write_begin(). + +However, given that netfs_unlock_abandoned_read_pages() is called _after_ +NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to +dereference is the one specified by ->no_unlock_folio as ownership +immediately reverts to the caller. + +Fix this by storing the folio pointer instead and using that rather than +the index. Also fix netfs_unlock_read_folio() where the same applies. + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com +cc: Paulo Alcantara +cc: Viacheslav Dubeyko +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 4 ++-- + fs/netfs/read_collect.c | 2 +- + fs/netfs/read_retry.c | 2 +- + include/linux/netfs.h | 2 +- + 4 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 004d426c02b41..83d0b8153e96e 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -670,7 +670,7 @@ int netfs_write_begin(struct netfs_inode *ctx, + ret = PTR_ERR(rreq); + goto error; + } +- rreq->no_unlock_folio = folio->index; ++ rreq->no_unlock_folio = folio; + __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); + + ret = netfs_begin_cache_read(rreq, ctx); +@@ -736,7 +736,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, + goto error; + } + +- rreq->no_unlock_folio = folio->index; ++ rreq->no_unlock_folio = folio; + __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); + ret = netfs_begin_cache_read(rreq, ctx); + if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) +diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c +index 3c9b847885c2a..23660a5901246 100644 +--- a/fs/netfs/read_collect.c ++++ b/fs/netfs/read_collect.c +@@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, + } + + just_unlock: +- if (folio->index == rreq->no_unlock_folio && ++ if (folio == rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { + _debug("no unlock"); + } else { +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index e10eb5a073326..f59a70f3a086b 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -292,7 +292,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) + struct folio *folio = folioq_folio(p, slot); + + if (folio && !folioq_is_marked2(p, slot)) { +- if (folio->index == rreq->no_unlock_folio && ++ if (folio == rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, + &rreq->flags)) { + _debug("no unlock"); +diff --git a/include/linux/netfs.h b/include/linux/netfs.h +index 4fd1d796ad73b..243c0f7379388 100644 +--- a/include/linux/netfs.h ++++ b/include/linux/netfs.h +@@ -252,7 +252,7 @@ struct netfs_io_request { + unsigned long long collected_to; /* Point we've collected to */ + unsigned long long cleaned_to; /* Position we've cleaned folios to */ + unsigned long long abandon_to; /* Position to abandon folios to */ +- pgoff_t no_unlock_folio; /* Don't unlock this folio after read */ ++ const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ + unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ + unsigned int debug_id; + unsigned int rsize; /* Maximum read size (0 for none) */ +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch b/queue-7.0/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch new file mode 100644 index 0000000000..96452be429 --- /dev/null +++ b/queue-7.0/netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch @@ -0,0 +1,93 @@ +From fb8156c58fee4b286c9d18b1bf78c13f5638a2ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:52 +0100 +Subject: netfs: Fix read-gaps to remove netfs_folio from filled folio + +From: David Howells + +[ Upstream commit a41168aef634356a9b87ec44349e3c82835700a5 ] + +Fix netfs_read_gaps() to remove the netfs_folio record from the folio +record before marking the folio uptodate if it successfully fills the gaps +around the dirty data in a streaming write folio (dirty, but not uptodate). + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0x138b1 0x8b15d * + write 0x507ee 0x10df7 0x927c0 + write 0x19993 0x10e04 0x927c0 * + mapwrite 0x66214 0x1a253 0x927c0 + copy_range 0xb704 0x89b9 0x24429 0x79380 + write 0x2402b 0x144a2 0x90660 * + mapwrite 0x204d5 0x140a0 0x927c0 * + copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * + read 0 0x9157c 0x9157c + +on cifs with the default cache option. + +It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in +netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-16-dhowells@redhat.com +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index ebd84a6cc3f09..51f844bfbdff6 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -395,6 +395,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + { + struct netfs_io_request *rreq; + struct address_space *mapping = folio->mapping; ++ struct netfs_group *group = netfs_folio_group(folio); + struct netfs_folio *finfo = netfs_folio_info(folio); + struct netfs_inode *ctx = netfs_inode(mapping->host); + struct folio *sink = NULL; +@@ -461,6 +462,12 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) + + ret = netfs_wait_for_read(rreq); + if (ret >= 0) { ++ if (group) ++ folio_change_private(folio, group); ++ else ++ folio_detach_private(folio); ++ kfree(finfo); ++ trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); + flush_dcache_folio(folio); + folio_mark_uptodate(folio); + } +@@ -496,10 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) + struct netfs_inode *ctx = netfs_inode(mapping->host); + int ret; + +- if (folio_test_dirty(folio)) { +- trace_netfs_folio(folio, netfs_folio_trace_read_gaps); ++ if (folio_test_dirty(folio)) + return netfs_read_gaps(file, folio); +- } + + _enter("%lx", folio->index); + +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-streaming-write-being-overwritten.patch b/queue-7.0/netfs-fix-streaming-write-being-overwritten.patch new file mode 100644 index 0000000000..5107a94e24 --- /dev/null +++ b/queue-7.0/netfs-fix-streaming-write-being-overwritten.patch @@ -0,0 +1,176 @@ +From 6d85e83092b08923099bf35f47af6d564c9448f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:50 +0100 +Subject: netfs: Fix streaming write being overwritten + +From: David Howells + +[ Upstream commit 7b4dcf1b9455a6e52ac7478b4057dbe10359576d ] + +In order to avoid reading whilst writing, netfslib will allow "streaming +writes" in which dirty data is stored directly into folios without reading +them first. Such folios are marked dirty but may not be marked uptodate. +If a folio is entirely written by a streaming write, uptodate will be set, +otherwise it will have a netfs_folio struct attached to ->private recording +the dirty region. + +In the event that a partially written streaming write page is to be +overwritten entirely by a single write(), netfs_perform_write() will try to +copy over it, but doesn't discard the netfs_folio if it succeeds; further, +it doesn't correctly handle a partial copy that overwrites some of the +dirty data. + +Fix this by the following: + + (1) If the folio is successfully overwritten, free the netfs_folio struct + before marking the page uptodate. + + (2) If the copy to the folio partially fails, but short of the dirty data, + just ignore the copy. + + (3) If the copy partially fails and overwrites some of the dirty data, + accept the copy, update the netfs_folio struct to record the new data. + If the folio is now filled, free the netfs_folio and set uptodate, + otherwise return a partial write. + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0 0x927c0 + write 0x63fb8 0x53c8 0 + copy_range 0xb704 0x19b9 0x24429 0x79380 + write 0x2402b 0x144a2 0x90660 * + write 0x204d5 0x140a0 0x927c0 * + copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * + read 0x00000 0x20000 0x9157c + read 0x20000 0x20000 0x9157c + read 0x40000 0x20000 0x9157c + read 0x60000 0x20000 0x9157c + read 0x7e1a0 0xcfb9 0x9157c + +on cifs with the default cache option. + +It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in +netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- + include/trace/events/netfs.h | 3 +++ + 2 files changed, 37 insertions(+), 13 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index a695d5168b2fc..0ff4c790ae263 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -247,18 +247,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + /* See if we can write a whole folio in one go. */ + if (!maybe_trouble && offset == 0 && part >= flen) { + copied = copy_folio_from_iter_atomic(folio, offset, part, iter); +- if (unlikely(copied == 0)) ++ if (likely(copied == part)) { ++ if (finfo) { ++ trace = netfs_whole_folio_modify_filled; ++ goto folio_now_filled; ++ } ++ __netfs_set_group(folio, netfs_group); ++ folio_mark_uptodate(folio); ++ trace = netfs_whole_folio_modify; ++ goto copied; ++ } ++ if (copied == 0) + goto copy_failed; +- if (unlikely(copied < part)) { ++ if (!finfo || copied <= finfo->dirty_offset) { + maybe_trouble = true; + iov_iter_revert(iter, copied); + copied = 0; + folio_unlock(folio); + goto retry; + } +- __netfs_set_group(folio, netfs_group); +- folio_mark_uptodate(folio); +- trace = netfs_whole_folio_modify; ++ ++ /* We overwrote some existing dirty data, so we have to ++ * accept the partial write. ++ */ ++ finfo->dirty_len += finfo->dirty_offset; ++ if (finfo->dirty_len == flen) { ++ trace = netfs_whole_folio_modify_filled_efault; ++ goto folio_now_filled; ++ } ++ if (copied > finfo->dirty_len) ++ finfo->dirty_len = copied; ++ finfo->dirty_offset = 0; ++ trace = netfs_whole_folio_modify_efault; + goto copied; + } + +@@ -328,16 +348,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto copy_failed; + finfo->dirty_len += copied; + if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { +- if (finfo->netfs_group) +- folio_change_private(folio, finfo->netfs_group); +- else +- folio_detach_private(folio); +- folio_mark_uptodate(folio); +- kfree(finfo); + trace = netfs_streaming_cont_filled_page; +- } else { +- trace = netfs_streaming_write_cont; ++ goto folio_now_filled; + } ++ trace = netfs_streaming_write_cont; + goto copied; + } + +@@ -351,6 +365,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + goto out; + continue; + ++ folio_now_filled: ++ if (finfo->netfs_group) ++ folio_change_private(folio, finfo->netfs_group); ++ else ++ folio_detach_private(folio); ++ folio_mark_uptodate(folio); ++ kfree(finfo); + copied: + trace_netfs_folio(folio, trace); + flush_dcache_folio(folio); +diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h +index 88d814ba1e697..db045135406c9 100644 +--- a/include/trace/events/netfs.h ++++ b/include/trace/events/netfs.h +@@ -177,6 +177,9 @@ + EM(netfs_folio_is_uptodate, "mod-uptodate") \ + EM(netfs_just_prefetch, "mod-prefetch") \ + EM(netfs_whole_folio_modify, "mod-whole-f") \ ++ EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ ++ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ ++ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ + EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_streaming_write, "mod-streamw") \ + EM(netfs_streaming_write_cont, "mod-streamw+") \ +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch b/queue-7.0/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch new file mode 100644 index 0000000000..ec236faad9 --- /dev/null +++ b/queue-7.0/netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch @@ -0,0 +1,169 @@ +From efa512d1fc690eaf228b29acf4192f8e6ff66c3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:44 +0100 +Subject: netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call + +From: Viacheslav Dubeyko + +[ Upstream commit dc7832d05deb4d632e8035e3299e31a3528fa0d0 ] + +The multiple runs of generic/013 test-case is capable +to reproduce a kernel BUG at mm/filemap.c:1504 with +probability of 30%. + +while true; do + sudo ./check generic/013 +done + +[ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 +[ 9849.452412] memcg:ffff8881a1915800 +[ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" +[ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) +[ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 +[ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 +[ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) +[ 9849.452474] ------------[ cut here ]------------ +[ 9849.452476] kernel BUG at mm/filemap.c:1504! +[ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI +[ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) +[ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 +0/2025 +[ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc +cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 +[ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 +[ 9849.504459] PKRU: 55555554 +[ 9849.504626] Call Trace: +[ 9849.505242] +[ 9849.505379] netfs_write_begin+0x7c8/0x10a0 +[ 9849.505877] ? __kasan_check_read+0x11/0x20 +[ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 +[ 9849.507178] ceph_write_begin+0x8c/0x1c0 +[ 9849.507934] generic_perform_write+0x391/0x8f0 +[ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 +[ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 +[ 9849.509581] ? ceph_get_caps+0x63/0xf0 +[ 9849.510259] ? ceph_get_caps+0x63/0xf0 +[ 9849.510530] ceph_write_iter+0xe79/0x1ae0 +[ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 +[ 9849.511839] ? lock_acquire+0x1ad/0x310 +[ 9849.512334] ? ksys_write+0xf9/0x230 +[ 9849.512582] ? lock_is_held_type+0xaa/0x140 +[ 9849.513128] vfs_write+0x512/0x1110 +[ 9849.513634] ? __fget_files+0x33/0x350 +[ 9849.513893] ? __pfx_vfs_write+0x10/0x10 +[ 9849.514143] ? mutex_lock_nested+0x1b/0x30 +[ 9849.514394] ksys_write+0xf9/0x230 +[ 9849.514621] ? __pfx_ksys_write+0x10/0x10 +[ 9849.514887] ? do_syscall_64+0x25e/0x1520 +[ 9849.515122] ? __kasan_check_read+0x11/0x20 +[ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.515655] __x64_sys_write+0x72/0xd0 +[ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 +[ 9849.516130] x64_sys_call+0x22f/0x2390 +[ 9849.516341] do_syscall_64+0x12b/0x1520 +[ 9849.516545] ? do_syscall_64+0x27c/0x1520 +[ 9849.516783] ? do_syscall_64+0x27c/0x1520 +[ 9849.517003] ? lock_release+0x318/0x480 +[ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 +[ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 +[ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.518073] ? do_syscall_64+0x25e/0x1520 +[ 9849.518291] ? __kasan_check_read+0x11/0x20 +[ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.518799] ? do_syscall_64+0x27c/0x1520 +[ 9849.519024] ? local_clock_noinstr+0xf/0x120 +[ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.519544] ? do_syscall_64+0x25e/0x1520 +[ 9849.519781] ? __kasan_check_read+0x11/0x20 +[ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520273] ? do_syscall_64+0x27c/0x1520 +[ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 +[ 9849.520767] ? irqentry_exit+0x10c/0x6c0 +[ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 +[ 9849.521224] ? exc_page_fault+0xab/0x130 +[ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e +[ 9849.521766] RIP: 0033:0x7e36cbd14907 +[ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 +[ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 +[ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 +[ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 +[ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 +[ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 +[ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 +[ 9849.525447] +[ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore +[ 9849.529150] ---[ end trace 0000000000000000 ]--- +[ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 +[ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 +[ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 +[ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 +[ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 +[ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 +[ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 +[ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 +[ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 +[ 9849.548679] PKRU: 55555554 + +The race sequence: +1. Read completes -> netfs_read_collection() runs +2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...) +3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin() +4. The netfs_unlock_abandoned_read_pages() unlocks the folio +5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO() + +The key reason of the issue that netfs_unlock_abandoned_read_pages() +doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes +folio_unlock() unconditionally. This patch implements in +netfs_unlock_abandoned_read_pages() logic similar to +netfs_unlock_read_folio(). + +Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") +Signed-off-by: Viacheslav Dubeyko +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com +Reviewed-by: Paulo Alcantara (Red Hat) +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +cc: Ceph Development +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/read_retry.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c +index 5ec548b996d65..e10eb5a073326 100644 +--- a/fs/netfs/read_retry.c ++++ b/fs/netfs/read_retry.c +@@ -292,8 +292,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) + struct folio *folio = folioq_folio(p, slot); + + if (folio && !folioq_is_marked2(p, slot)) { +- trace_netfs_folio(folio, netfs_folio_trace_abandon); +- folio_unlock(folio); ++ if (folio->index == rreq->no_unlock_folio && ++ test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, ++ &rreq->flags)) { ++ _debug("no unlock"); ++ } else { ++ trace_netfs_folio(folio, ++ netfs_folio_trace_abandon); ++ folio_unlock(folio); ++ } + } + } + } +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch b/queue-7.0/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch new file mode 100644 index 0000000000..d52a98f7c8 --- /dev/null +++ b/queue-7.0/netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch @@ -0,0 +1,80 @@ +From 4dd51a2a996314e9ca7e20d6210f8d9af16d65bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:53 +0100 +Subject: netfs: Fix write streaming disablement if fd open O_RDWR + +From: David Howells + +[ Upstream commit 70a7b9193bbbfceaab5974de66834c64ccc875dd ] + +In netfs_perform_write(), "write streaming" (the caching of dirty data in +dirty but !uptodate folios) is performed to avoid the need to read data +that is just going to get immediately overwritten. However, this is/will +be disabled in three circumstances: if the fd is open O_RDWR, if fscache is +in use (as we need to round out the blocks for DIO) or if content +encryption is enabled (again for rounding out purposes). + +The idea behind disabling it if the fd is open O_RDWR is that we'd need to +flush the write-streaming page before we could read the data, particularly +through mmap. But netfs now fills in the gaps if ->read_folio() is called +on the page, so that is unnecessary. Further, this doesn't actually work +if a separate fd is open for reading. + +Fix this by removing the check for O_RDWR, thereby allowing streaming +writes even when we might read. + +This caused a number of problems with the generic/522 xfstest, but those +are now fixed. + +Fixes: c38f4e96e605 ("netfs: Provide func to copy data to pagecache for buffered write") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_write.c | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) + +diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c +index 0ff4c790ae263..b606c3bd84bcd 100644 +--- a/fs/netfs/buffered_write.c ++++ b/fs/netfs/buffered_write.c +@@ -204,11 +204,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + } + + /* Decide how we should modify a folio. We might be attempting +- * to do write-streaming, in which case we don't want to a +- * local RMW cycle if we can avoid it. If we're doing local +- * caching or content crypto, we award that priority over +- * avoiding RMW. If the file is open readably, then we also +- * assume that we may want to read what we wrote. ++ * to do write-streaming, as we don't want to a local RMW cycle ++ * if we can avoid it. If we're doing local caching or content ++ * crypto, we award that priority over avoiding RMW. If the ++ * file is open readably, then we let ->read_folio() fill in ++ * the gaps. + */ + finfo = netfs_folio_info(folio); + group = netfs_folio_group(folio); +@@ -284,12 +284,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, + + /* We don't want to do a streaming write on a file that loses + * caching service temporarily because the backing store got +- * culled and we don't really want to get a streaming write on +- * a file that's open for reading as ->read_folio() then has to +- * be able to flush it. ++ * culled. + */ +- if ((file->f_mode & FMODE_READ) || +- netfs_is_cache_enabled(ctx)) { ++ if (netfs_is_cache_enabled(ctx)) { + if (finfo) { + netfs_stat(&netfs_n_wh_wstream_conflict); + goto flush_content; +-- +2.53.0 + diff --git a/queue-7.0/netfs-fix-zeropoint-update-where-i_size-remote_i_siz.patch b/queue-7.0/netfs-fix-zeropoint-update-where-i_size-remote_i_siz.patch new file mode 100644 index 0000000000..0af585b245 --- /dev/null +++ b/queue-7.0/netfs-fix-zeropoint-update-where-i_size-remote_i_siz.patch @@ -0,0 +1,80 @@ +From 1035ae949ecfa930bb5812d3578aa11f0e4d9e02 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 13:33:43 +0100 +Subject: netfs: Fix zeropoint update where i_size > remote_i_size + +From: David Howells + +[ Upstream commit 4543a4d737944134a1394afe797622546fbcc98a ] + +Fix the update of the zero point[*] by netfs_release_folio() when there is +uncommitted data in the pagecache beyond the folio being released but the +on-server EOF is in this folio (ie. i_size > remote_i_size). The update +needs to limit zero_point to remote_i_size, not i_size as i_size is a local +phenomenon reflecting updates made locally to the pagecache, not stuff +written to the server. remote_i_size tracks the server's i_size. + +[*] The zero point is the file position from which we can assume that the + server will just return zeros, so we can avoid generating reads. + +Note that netfs_invalidate_folio() probably doesn't need fixing as +zero_point should be updated by setattr after truncation or fallocate. + +Found with: + + fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ + /xfstest.test/junk --replay-ops=junk.fsxops + +using the following as junk.fsxops: + + truncate 0x0 0x1bbae 0x82864 + write 0x3ef2e 0xf9c8 0x1bbae + write 0x67e05 0xcb5a 0x4e8f6 + mapread 0x57781 0x85b6 0x7495f + copy_range 0x5d3d 0x10329 0x54fac 0x7495f + write 0x64710 0x1c2b 0x7495f + mapread 0x64000 0x1000 0x7495f + +on cifs with the default cache option. + +It shows read-gaps on folio 0x64 failing with a short read (ie. it hits +EOF) if the FMODE_READ check is commented out in netfs_perform_write(): + + if (//(file->f_mode & FMODE_READ) || + netfs_is_cache_enabled(ctx)) { + +and no fscache. This was initially found with the generic/522 xfstest. + +Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netfs_inval_folio()") +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260512123404.719402-7-dhowells@redhat.com +cc: Paulo Alcantara +cc: Matthew Wilcox +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/netfs/misc.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c +index bad661ff2bec8..723571ca1b885 100644 +--- a/fs/netfs/misc.c ++++ b/fs/netfs/misc.c +@@ -307,10 +307,10 @@ bool netfs_release_folio(struct folio *folio, gfp_t gfp) + return false; + + netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); +- end = umin(folio_next_pos(folio), i_size); ++ end = folio_next_pos(folio); + if (end > zero_point) { + spin_lock(&inode->i_lock); +- end = umin(folio_next_pos(folio), inode->i_size); ++ end = umin(end, ctx->_remote_i_size); + if (end > ctx->_zero_point) + netfs_write_zero_point(inode, end); + spin_unlock(&inode->i_lock); +-- +2.53.0 + diff --git a/queue-7.0/nfsd-fix-infinite-loop-in-layout-state-revocation.patch b/queue-7.0/nfsd-fix-infinite-loop-in-layout-state-revocation.patch new file mode 100644 index 0000000000..fd50e0f349 --- /dev/null +++ b/queue-7.0/nfsd-fix-infinite-loop-in-layout-state-revocation.patch @@ -0,0 +1,46 @@ +From 7c74be228873f7379973da6d32886a77b257d7d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Apr 2026 14:52:59 -0400 +Subject: NFSD: Fix infinite loop in layout state revocation + +From: Chuck Lever + +[ Upstream commit 4f8ef58c10bfe5f86a643c7c8331b37e69e3dae1 ] + +find_one_sb_stid() skips stids whose sc_status is non-zero, but the +SC_TYPE_LAYOUT case in nfsd4_revoke_states() never sets sc_status +before calling nfsd4_close_layout(). The retry loop therefore finds +the same layout stid on every iteration, hanging the revoker +indefinitely. + +Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") +Reported-by: Dai Ngo +Reviewed-by: Jeff Layton +Tested-by: Dai Ngo +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 44b1a93f219af..530459dfa7606 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -1850,6 +1850,13 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) + break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(stid); ++ spin_lock(&clp->cl_lock); ++ if (stid->sc_status == 0) { ++ stid->sc_status |= ++ SC_STATUS_ADMIN_REVOKED; ++ atomic_inc(&clp->cl_admin_revoked); ++ } ++ spin_unlock(&clp->cl_lock); + nfsd4_close_layout(ls); + break; + } +-- +2.53.0 + diff --git a/queue-7.0/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch b/queue-7.0/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch new file mode 100644 index 0000000000..d26e8eec03 --- /dev/null +++ b/queue-7.0/nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch @@ -0,0 +1,43 @@ +From ce34e2b1cfe7cae2deff488956f6c9a1dbfc7edb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 19:23:01 +0800 +Subject: nsfs: fix wrong error code returned for pidns ioctls + +From: Zhihao Cheng + +[ Upstream commit 725ecd80688bf3c57ca9205431f2c06174ff0756 ] + +When executing NS_GET_PID_FROM_PIDNS (or similar pidns ioctls), if the +target task cannot be found in the corresponding pid_ns, the error code +should be ESRCH instead of ENOTTY. + +This bug was introduced when the extensible ioctl handling was added. +Without proper return, ret would be overwritten by the default case in +the extensible ioctl switch statement. + +Fixes: a1d220d9dafa8 ("nsfs: iterate through mount namespaces") +Signed-off-by: Zhihao Cheng +Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com +Reviewed-by: Yang Erkun +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/nsfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/nsfs.c b/fs/nsfs.c +index c215878d55e87..fb0dcc1196699 100644 +--- a/fs/nsfs.c ++++ b/fs/nsfs.c +@@ -266,7 +266,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl, + else + tsk = find_task_by_pid_ns(arg, pid_ns); + if (!tsk) +- break; ++ return ret; + + switch (ioctl) { + case NS_GET_PID_FROM_PIDNS: +-- +2.53.0 + diff --git a/queue-7.0/nvme-fix-bio-leak-on-mapping-failure.patch b/queue-7.0/nvme-fix-bio-leak-on-mapping-failure.patch new file mode 100644 index 0000000000..b22cf72f6d --- /dev/null +++ b/queue-7.0/nvme-fix-bio-leak-on-mapping-failure.patch @@ -0,0 +1,48 @@ +From 3909e888120bba30b41fa42b50b57072e5b6bcf9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 06:16:02 -0700 +Subject: nvme: fix bio leak on mapping failure + +From: Keith Busch + +[ Upstream commit 2279cd9c61a330e5de4d6eb0bc422820dd6fdf36 ] + +The local bio is always NULL, so we'd leak the bio if the integrity +mapping failed. Just get it directly from the request. + +Fixes: d0d1d522316e91f ("blk-map: provide the bdev to bio if one exists") +Reviewed-by: Sagi Grimberg +Reviewed-by: John Garry +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/ioctl.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c +index 8844bbd395159..77c668282d996 100644 +--- a/drivers/nvme/host/ioctl.c ++++ b/drivers/nvme/host/ioctl.c +@@ -122,7 +122,6 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); + struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; + bool has_metadata = meta_buffer && meta_len; +- struct bio *bio = NULL; + int ret; + + if (!nvme_ctrl_sgl_supported(ctrl)) +@@ -154,8 +153,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, + return ret; + + out_unmap: +- if (bio) +- blk_rq_unmap_user(bio); ++ if (req->bio) ++ blk_rq_unmap_user(req->bio); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/nvme-pci-fix-dma-mapping-leak-on-data-setup-error.patch b/queue-7.0/nvme-pci-fix-dma-mapping-leak-on-data-setup-error.patch new file mode 100644 index 0000000000..a6cbf8ac24 --- /dev/null +++ b/queue-7.0/nvme-pci-fix-dma-mapping-leak-on-data-setup-error.patch @@ -0,0 +1,103 @@ +From f96afb930a99144a77566ce8d02b42acf2f1440b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 13:01:57 -0700 +Subject: nvme-pci: fix dma mapping leak on data setup error + +From: Keith Busch + +[ Upstream commit 1bf86336e4b6cf40873fda47a7fe191446864937 ] + +We're leaking the initial DMA mapping during iteration if we fail to +allocate the tracking descriptor for both PRP and SGL. Unmap the +iterator directly; we can't use the existing unmap helper because it +depends on the tracking descriptor being successfully allocated, so a +new one for an in-use iterator is provided. + +The mappings were also leaking when the driver detects an invalid +bio_vec when mapping PRPs, so fix that too. + +Fixes: b8b7570a7ec87 ("nvme-pci: fix dma unmapping when using PRPs and not using the IOVA mapping") +Fixes: 7ce3c1dd78fca ("nvme-pci: convert the data mapping to blk_rq_dma_map") +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 5b998db940bd6..a0e9767bc21e6 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -997,6 +997,23 @@ static bool nvme_pci_prp_iter_next(struct request *req, struct device *dma_dev, + return nvme_pci_prp_save_mapping(req, dma_dev, iter); + } + ++static void nvme_unmap_iter(struct request *req, struct blk_dma_iter *iter, ++ struct dma_iova_state *state) ++{ ++ struct nvme_queue *nvmeq = req->mq_hctx->driver_data; ++ struct device *dev = nvmeq->dev->dev; ++ ++ if (!blk_rq_dma_unmap(req, dev, state, iter->len, iter->p2pdma.map)) { ++ unsigned int attrs = 0; ++ ++ if (iter->p2pdma.map == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE) ++ attrs |= DMA_ATTR_MMIO; ++ ++ dma_unmap_phys(dev, iter->addr, iter->len, rq_dma_dir(req), ++ attrs); ++ } ++} ++ + static blk_status_t nvme_pci_setup_data_prp(struct request *req, + struct blk_dma_iter *iter) + { +@@ -1007,8 +1024,10 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req, + unsigned int prp_len, i; + __le64 *prp_list; + +- if (!nvme_pci_prp_save_mapping(req, nvmeq->dev->dev, iter)) ++ if (!nvme_pci_prp_save_mapping(req, nvmeq->dev->dev, iter)) { ++ nvme_unmap_iter(req, iter, &iod->dma_state); + return iter->status; ++ } + + /* + * PRP1 always points to the start of the DMA transfers. +@@ -1113,6 +1132,7 @@ static blk_status_t nvme_pci_setup_data_prp(struct request *req, + dev_err_once(nvmeq->dev->dev, + "Incorrectly formed request for payload:%d nents:%d\n", + blk_rq_payload_bytes(req), blk_rq_nr_phys_segments(req)); ++ nvme_unmap_data(req); + return BLK_STS_IOERR; + } + +@@ -1156,8 +1176,11 @@ static blk_status_t nvme_pci_setup_data_sgl(struct request *req, + + sg_list = dma_pool_alloc(nvme_dma_pool(nvmeq, iod), GFP_ATOMIC, + &sgl_dma); +- if (!sg_list) ++ if (!sg_list) { ++ nvme_unmap_iter(req, iter, &iod->dma_state); + return BLK_STS_RESOURCE; ++ } ++ + iod->descriptors[iod->nr_descriptors++] = sg_list; + + do { +@@ -1314,8 +1337,10 @@ static blk_status_t nvme_pci_setup_meta_iter(struct request *req) + + sg_list = dma_pool_alloc(nvmeq->descriptor_pools.small, GFP_ATOMIC, + &sgl_dma); +- if (!sg_list) ++ if (!sg_list) { ++ nvme_unmap_iter(req, &iter, &iod->meta_dma_state); + return BLK_STS_RESOURCE; ++ } + + iod->meta_descriptor = sg_list; + iod->meta_dma = sgl_dma; +-- +2.53.0 + diff --git a/queue-7.0/nvme-pci-fix-dma_vecs-leak-on-p2p-memory.patch b/queue-7.0/nvme-pci-fix-dma_vecs-leak-on-p2p-memory.patch new file mode 100644 index 0000000000..a81a907e5b --- /dev/null +++ b/queue-7.0/nvme-pci-fix-dma_vecs-leak-on-p2p-memory.patch @@ -0,0 +1,37 @@ +From 192da0cb2051885ee078baccf208b07b8dd8cb3b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 18:03:44 -0700 +Subject: nvme-pci: fix dma_vecs leak on p2p memory + +From: Keith Busch + +[ Upstream commit 85686c72966c5ee637893f124ddb31a1cace7bee ] + +We don't unmap P2P memory, so we don't need to track it. The dma_vec +allocation was getting leaked on the completion. + +Fixes: b8b7570a7ec87 ("nvme-pci: fix dma unmapping when using PRPs and not using the IOVA mapping") +Reviewed-by: Christoph Hellwig +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 6d522c52dca67..5b998db940bd6 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -966,7 +966,8 @@ static bool nvme_pci_prp_save_mapping(struct request *req, + { + struct nvme_iod *iod = blk_mq_rq_to_pdu(req); + +- if (dma_use_iova(&iod->dma_state) || !dma_need_unmap(dma_dev)) ++ if (dma_use_iova(&iod->dma_state) || !dma_need_unmap(dma_dev) || ++ (iod->flags & IOD_DATA_P2P)) + return true; + + if (!iod->nr_dma_vecs) { +-- +2.53.0 + diff --git a/queue-7.0/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch b/queue-7.0/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch new file mode 100644 index 0000000000..86c1d1016f --- /dev/null +++ b/queue-7.0/nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch @@ -0,0 +1,90 @@ +From ea2b0281a1a7e2bf9a944cba3f3ba870737b6408 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 16:11:16 +0800 +Subject: nvme-pci: fix use-after-free in nvme_free_host_mem() + +From: Chia-Lin Kao (AceLan) + +[ Upstream commit b35a13036755c5803168a7cb93bc66035c3e65b8 ] + +nvme_free_host_mem() frees dev->hmb_sgt via dma_free_noncontiguous() +but never clears the pointer afterward. This leads to a use-after-free +if nvme_free_host_mem() is called twice in the same error path. + +This can happen during nvme_probe() when nvme_setup_host_mem() succeeds +in allocating the HMB (setting dev->hmb_sgt) but nvme_set_host_mem() +fails with an I/O error: + + nvme_setup_host_mem() + nvme_alloc_host_mem_single() -> sets dev->hmb_sgt + nvme_set_host_mem() -> fails with -EIO + nvme_free_host_mem() -> frees hmb_sgt, but does NOT NULL it + return error + + nvme_probe() error path: + nvme_free_host_mem() -> dev->hmb_sgt is stale, use-after-free + +The second call dereferences the freed sgt, causing a NULL pointer +dereference in iommu_dma_free_noncontiguous() when it accesses +sgt->sgl->dma_address (the backing memory has been freed and zeroed). + +This is reproducible on Thunderbolt-attached NVMe devices (e.g., OWC +Envoy Express behind a Dell WD22TB4 dock) where the device intermittently +returns I/O errors during HMB setup due to PCIe link instability. + + BUG: kernel NULL pointer dereference, address: 0000000000000010 + RIP: 0010:iommu_dma_free_noncontiguous+0x22/0x80 + Call Trace: + + dma_free_noncontiguous+0x3b/0x130 + nvme_free_host_mem+0x30/0xf0 [nvme] + nvme_probe.cold+0xcc/0x275 [nvme] + local_pci_probe+0x43/0xa0 + pci_device_probe+0xeea/0x290 + really_probe+0xf9/0x3b0 + __driver_probe_device+0x8b/0x170 + driver_probe_device+0x24/0xd0 + __driver_attach_async_helper+0x6b/0x110 + async_run_entry_fn+0x37/0x170 + process_one_work+0x1ac/0x3d0 + worker_thread+0x1b8/0x360 + kthread+0xf7/0x130 + ret_from_fork+0x2d8/0x3a0 + ret_from_fork_asm+0x1a/0x30 + + +Fix this by setting dev->hmb_sgt to NULL after freeing it, so the +second call takes the multi-descriptor path which safely handles the +already-cleaned-up state. + +Fixes: 63a5c7a4b4c4 ("nvme-pci: use dma_alloc_noncontigous if possible") +Signed-off-by: Chia-Lin Kao (AceLan) +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 4c052ed18cb8d..6d522c52dca67 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -2533,11 +2533,13 @@ static void nvme_free_host_mem_multi(struct nvme_dev *dev) + + static void nvme_free_host_mem(struct nvme_dev *dev) + { +- if (dev->hmb_sgt) ++ if (dev->hmb_sgt) { + dma_free_noncontiguous(dev->dev, dev->host_mem_size, + dev->hmb_sgt, DMA_BIDIRECTIONAL); +- else ++ dev->hmb_sgt = NULL; ++ } else { + nvme_free_host_mem_multi(dev); ++ } + + dma_free_coherent(dev->dev, dev->host_mem_descs_size, + dev->host_mem_descs, dev->host_mem_descs_dma); +-- +2.53.0 + diff --git a/queue-7.0/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch b/queue-7.0/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch new file mode 100644 index 0000000000..fde24d6419 --- /dev/null +++ b/queue-7.0/octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch @@ -0,0 +1,42 @@ +From ebcfa7b6038370e61a94952e16ddc3c30e3e67c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 10:00:36 +0530 +Subject: octeontx2-af: npc: Fix allmulticast skip logic for LBK and SDP VFs + +From: Ratheesh Kannoth + +[ Upstream commit 9eddc819f00b5b74bb4ac91396f80bd35f5f3561 ] + +When installing the allmulticast NPC rule, rvu_npc_install_allmulti_entry() +should skip LBK and SDP VFs (only CGX PF/VF may add the entry). The +code combined is_lbk_vf() and is_sdp_vf() with logical AND, which is +never true for a single pcifunc, so the intended early return never ran. + +Use logical OR instead. + +Cc: Geetha sowjanya +Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks") +Signed-off-by: Ratheesh Kannoth +Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index 8658cb2143dfc..e28675fe18907 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -837,7 +837,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, + u16 vf_func; + + /* Only CGX PF/VF can add allmulticast entry */ +- if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc)) ++ if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc)) + return; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); +-- +2.53.0 + diff --git a/queue-7.0/ovpn-disable-bhs-when-updating-device-stats.patch b/queue-7.0/ovpn-disable-bhs-when-updating-device-stats.patch new file mode 100644 index 0000000000..85e95ad9b8 --- /dev/null +++ b/queue-7.0/ovpn-disable-bhs-when-updating-device-stats.patch @@ -0,0 +1,183 @@ +From 861a8e683a72694a9c069cf23d341483699a4963 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 15:26:10 +0200 +Subject: ovpn: disable BHs when updating device stats + +From: Ralf Lici + +[ Upstream commit 0c0dddc07d272a8d25922e48041e8e4d2434df7e ] + +ovpn updates dev->dstats from both process and softirq contexts. In +particular, TCP paths may run from socket callbacks, workqueues or +strparser work, while UDP receive and ovpn's ndo_start_xmit path may +update the same per-device dstats from BH context. + +Add ovpn device drop-stat helpers that disable BHs around +dev_dstats_rx_dropped() and dev_dstats_tx_dropped(), and use them for +drop accounting. + +The successful RX dev_dstats_rx_add() update is already covered by the +BH-disabled section around gro_cells_receive(). For the successful TCP +TX dev_dstats_tx_add() update, replace the existing preempt-disabled +section with a BH-disabled one. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Signed-off-by: Ralf Lici +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/io.c | 12 ++++++------ + drivers/net/ovpn/stats.h | 16 ++++++++++++++++ + drivers/net/ovpn/tcp.c | 10 +++++----- + drivers/net/ovpn/udp.c | 2 +- + 4 files changed, 28 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c +index 955c9a37e1f8d..c03e58e28a860 100644 +--- a/drivers/net/ovpn/io.c ++++ b/drivers/net/ovpn/io.c +@@ -196,7 +196,7 @@ void ovpn_decrypt_post(void *data, int ret) + skb = NULL; + drop: + if (unlikely(skb)) +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + kfree_skb(skb); + drop_nocount: + if (likely(peer)) +@@ -220,7 +220,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) + net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n", + netdev_name(peer->ovpn->dev), peer->id, + key_id); +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + kfree_skb(skb); + ovpn_peer_put(peer); + return; +@@ -298,7 +298,7 @@ void ovpn_encrypt_post(void *data, int ret) + rcu_read_unlock(); + err: + if (unlikely(skb)) +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + if (likely(peer)) + ovpn_peer_put(peer); + if (likely(ks)) +@@ -340,7 +340,7 @@ static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb, + */ + skb_list_walk_safe(skb, curr, next) { + if (unlikely(!ovpn_encrypt_one(peer, curr))) { +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + kfree_skb(curr); + } + } +@@ -411,7 +411,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) + if (unlikely(!curr)) { + net_err_ratelimited("%s: skb_share_check failed for payload packet\n", + netdev_name(dev)); +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + continue; + } + +@@ -437,7 +437,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) + drop: + ovpn_peer_put(peer); + drop_no_peer: +- dev_dstats_tx_dropped(ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(ovpn->dev); + skb_tx_error(skb); + kfree_skb_list(skb); + return NETDEV_TX_OK; +diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h +index 53433d8b6c331..3a45b97c00568 100644 +--- a/drivers/net/ovpn/stats.h ++++ b/drivers/net/ovpn/stats.h +@@ -11,6 +11,8 @@ + #ifndef _NET_OVPN_OVPNSTATS_H_ + #define _NET_OVPN_OVPNSTATS_H_ + ++#include ++ + /* one stat */ + struct ovpn_peer_stat { + atomic64_t bytes; +@@ -44,4 +46,18 @@ static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats, + ovpn_peer_stats_increment(&stats->tx, n); + } + ++static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev) ++{ ++ local_bh_disable(); ++ dev_dstats_tx_dropped(dev); ++ local_bh_enable(); ++} ++ ++static inline void ovpn_dev_dstats_rx_dropped(struct net_device *dev) ++{ ++ local_bh_disable(); ++ dev_dstats_rx_dropped(dev); ++ local_bh_enable(); ++} ++ + #endif /* _NET_OVPN_OVPNSTATS_H_ */ +diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c +index 5f345ae7d59d2..505c2f214c9f1 100644 +--- a/drivers/net/ovpn/tcp.c ++++ b/drivers/net/ovpn/tcp.c +@@ -152,7 +152,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) + if (WARN_ON(!ovpn_peer_hold(peer))) + goto err_nopeer; + schedule_work(&peer->tcp.defer_del_work); +- dev_dstats_rx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); + err_nopeer: + kfree_skb(skb); + } +@@ -298,9 +298,9 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) + } while (peer->tcp.out_msg.len > 0); + + if (!peer->tcp.out_msg.len) { +- preempt_disable(); ++ local_bh_disable(); + dev_dstats_tx_add(peer->ovpn->dev, skb->len); +- preempt_enable(); ++ local_bh_enable(); + } + + kfree_skb(peer->tcp.out_msg.skb); +@@ -331,7 +331,7 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk, + ovpn_tcp_send_sock(peer, sk); + + if (peer->tcp.out_msg.skb) { +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + kfree_skb(skb); + return; + } +@@ -353,7 +353,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, + if (sock_owned_by_user(sk)) { + if (skb_queue_len(&peer->tcp.out_queue) >= + READ_ONCE(net_hotdata.max_backlog)) { +- dev_dstats_tx_dropped(peer->ovpn->dev); ++ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); + kfree_skb(skb); + goto unlock; + } +diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c +index 272b535ecaad4..367563d84472f 100644 +--- a/drivers/net/ovpn/udp.c ++++ b/drivers/net/ovpn/udp.c +@@ -126,7 +126,7 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) + return 0; + + drop: +- dev_dstats_rx_dropped(ovpn->dev); ++ ovpn_dev_dstats_rx_dropped(ovpn->dev); + drop_noovpn: + kfree_skb(skb); + return 0; +-- +2.53.0 + diff --git a/queue-7.0/ovpn-fix-race-between-deleting-interface-and-adding-.patch b/queue-7.0/ovpn-fix-race-between-deleting-interface-and-adding-.patch new file mode 100644 index 0000000000..6d4bdc4779 --- /dev/null +++ b/queue-7.0/ovpn-fix-race-between-deleting-interface-and-adding-.patch @@ -0,0 +1,124 @@ +From a15514db20324f9888315f8a30245977a95764c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Mar 2026 14:47:56 +0100 +Subject: ovpn: fix race between deleting interface and adding new peer + +From: Antonio Quartulli + +[ Upstream commit 982422b11e6f95f766a8cd2c2b1cbdb77e234a61 ] + +While deleting an existing ovpn interface, there is a very +narrow window where adding a new peer via netlink may cause +the netdevice to hang and prevent its unregistration. + +It may happen during ovpn_dellink(), when all existing peers are +freed and the device is queued for deregistration, but a +CMD_PEER_NEW message comes in adding a new peer that takes again +a reference to the netdev. + +At this point there is no way to release the device because we are +under the assumption that all peers were already released. + +Fix the race condition by releasing all peers in ndo_uninit(), +when the netdevice has already been removed from the netdev +list. + +Also ovpn_peer_add() has now an extra check that forces the +function to bail out if the device reg_state is not REGISTERED. +This way any incoming CMD_PEER_NEW racing with the interface +deletion routine will simply stop before adding the peer. + +Note that the above check happens while holding the netdev_lock +to prevent racing netdev state changes. + +ovpn_dellink() is now empty and can be removed. + +Reported-by: Hyunwoo Kim +Closes: https://lore.kernel.org/netdev/aaVgJ16edTfQkYbx@v4bel/ +Suggested-by: Sabrina Dubroca +Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object") +Reviewed-by: Sabrina Dubroca +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/main.c | 12 ++---------- + drivers/net/ovpn/peer.c | 21 ++++++++++++++++++--- + 2 files changed, 20 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c +index 2e0420febda05..9993c1dfe471d 100644 +--- a/drivers/net/ovpn/main.c ++++ b/drivers/net/ovpn/main.c +@@ -92,6 +92,8 @@ static void ovpn_net_uninit(struct net_device *dev) + { + struct ovpn_priv *ovpn = netdev_priv(dev); + ++ disable_delayed_work_sync(&ovpn->keepalive_work); ++ ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); + gro_cells_destroy(&ovpn->gro_cells); + } + +@@ -208,15 +210,6 @@ static int ovpn_newlink(struct net_device *dev, + return register_netdevice(dev); + } + +-static void ovpn_dellink(struct net_device *dev, struct list_head *head) +-{ +- struct ovpn_priv *ovpn = netdev_priv(dev); +- +- cancel_delayed_work_sync(&ovpn->keepalive_work); +- ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); +- unregister_netdevice_queue(dev, head); +-} +- + static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev) + { + struct ovpn_priv *ovpn = netdev_priv(dev); +@@ -235,7 +228,6 @@ static struct rtnl_link_ops ovpn_link_ops = { + .policy = ovpn_policy, + .maxtype = IFLA_OVPN_MAX, + .newlink = ovpn_newlink, +- .dellink = ovpn_dellink, + .fill_info = ovpn_fill_info, + }; + +diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c +index f69694e00dcee..8cd129cc2142d 100644 +--- a/drivers/net/ovpn/peer.c ++++ b/drivers/net/ovpn/peer.c +@@ -1029,14 +1029,29 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer) + */ + int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer) + { ++ int ret = -ENODEV; ++ ++ /* Prevent adding new peers while destroying the ovpn interface. ++ * Failing to do so would end up holding the device reference ++ * endlessly hostage of the new peer object with no chance of ++ * release.. ++ */ ++ netdev_lock(ovpn->dev); ++ if (ovpn->dev->reg_state != NETREG_REGISTERED) ++ goto out; ++ + switch (ovpn->mode) { + case OVPN_MODE_MP: +- return ovpn_peer_add_mp(ovpn, peer); ++ ret = ovpn_peer_add_mp(ovpn, peer); ++ break; + case OVPN_MODE_P2P: +- return ovpn_peer_add_p2p(ovpn, peer); ++ ret = ovpn_peer_add_p2p(ovpn, peer); ++ break; + } ++out: ++ netdev_unlock(ovpn->dev); + +- return -EOPNOTSUPP; ++ return ret; + } + + /** +-- +2.53.0 + diff --git a/queue-7.0/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch b/queue-7.0/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch new file mode 100644 index 0000000000..3eb6699f5b --- /dev/null +++ b/queue-7.0/ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch @@ -0,0 +1,108 @@ +From 62e538fecfdb7970a74e3e7aee9490881861396a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 11:55:21 +0100 +Subject: ovpn: respect peer refcount in CMD_NEW_PEER error path + +From: David Carlier + +[ Upstream commit 1fef6614673ff0846d30acdeeaf3cf98bb5f6116 ] + +ovpn_nl_peer_new_doit()'s error path calls ovpn_peer_release() directly +rather than ovpn_peer_put(), bypassing the kref. The accompanying +comment ("peer was not yet hashed, thus it is not used in any context") +holds for UDP but not for TCP. + +For UDP, the ovpn_socket union uses the .ovpn arm and never points back +at a peer; UDP encap_recv looks up peers via the not-yet-populated +hashtables, so the new peer is unreachable until ovpn_peer_add() +publishes it. + +For TCP, ovpn_socket_new() sets ovpn_sock->peer and +ovpn_tcp_socket_attach() publishes ovpn_sock via rcu_assign_sk_user_data(). +From that moment until ovpn_socket_release() detaches in the error path, +the TCP fd is fully wired: userspace recvmsg / sendmsg / close / poll +on the fd, as well as the strparser-driven ovpn_tcp_rcv() path, can +reach the peer through sk_user_data -> ovpn_sock->peer and bump its +refcount via ovpn_peer_hold(). + +ovpn_tcp_socket_wait_finish() (called inside ovpn_socket_release()) +drains strparser and the tx work, but does not synchronize with +userspace syscall callers that already hold a peer reference. If +ovpn_nl_peer_modify() or ovpn_peer_add() returns an error while such +a caller is in flight - notably an ovpn_tcp_recvmsg() blocked in +__skb_recv_datagram() on peer->tcp.user_queue - the direct +ovpn_peer_release() destroys the peer while the caller still holds +the reference, and the eventual ovpn_peer_put() from that caller +operates on freed memory. + +Replace the direct destructor call with ovpn_peer_put() so the kref +correctly defers destruction until the last reference is dropped. +In the common case where no concurrent user is present, behaviour is +unchanged: the kref hits zero immediately and ovpn_peer_release_kref() +runs the same destructor. + +With this conversion ovpn_peer_release() has no callers outside peer.c +- ovpn_peer_release_kref() in the same translation unit is the only +remaining user - so make it static and drop its declaration from +peer.h. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Reviewed-by: Sabrina Dubroca +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: David Carlier +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/netlink.c | 8 +++++--- + drivers/net/ovpn/peer.c | 2 +- + drivers/net/ovpn/peer.h | 1 - + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c +index c7f3824376302..bdb56ef0c9040 100644 +--- a/drivers/net/ovpn/netlink.c ++++ b/drivers/net/ovpn/netlink.c +@@ -455,10 +455,12 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) + sock_release: + ovpn_socket_release(peer); + peer_release: +- /* release right away because peer was not yet hashed, thus it is not +- * used in any context ++ /* For UDP, the peer is unreachable until added to the hashtables, so ++ * dropping the initial reference is enough. For TCP, the peer may be ++ * concurrently reachable via sk_user_data->peer until ++ * ovpn_socket_release() detaches; rely on the refcount. + */ +- ovpn_peer_release(peer); ++ ovpn_peer_put(peer); + + return ret; + } +diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c +index 3716a1d828015..f69694e00dcee 100644 +--- a/drivers/net/ovpn/peer.c ++++ b/drivers/net/ovpn/peer.c +@@ -348,7 +348,7 @@ static void ovpn_peer_release_rcu(struct rcu_head *head) + * ovpn_peer_release - release peer private members + * @peer: the peer to release + */ +-void ovpn_peer_release(struct ovpn_peer *peer) ++static void ovpn_peer_release(struct ovpn_peer *peer) + { + ovpn_crypto_state_release(&peer->crypto); + spin_lock_bh(&peer->lock); +diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h +index a1423f2b09e06..4de5aeae33f7d 100644 +--- a/drivers/net/ovpn/peer.h ++++ b/drivers/net/ovpn/peer.h +@@ -125,7 +125,6 @@ static inline bool ovpn_peer_hold(struct ovpn_peer *peer) + return kref_get_unless_zero(&peer->refcount); + } + +-void ovpn_peer_release(struct ovpn_peer *peer); + void ovpn_peer_release_kref(struct kref *kref); + + /** +-- +2.53.0 + diff --git a/queue-7.0/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch b/queue-7.0/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch new file mode 100644 index 0000000000..0e907c4c76 --- /dev/null +++ b/queue-7.0/ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch @@ -0,0 +1,84 @@ +From df6119e4bd09e996f8abfc680f2ea89219d36eee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 11:55:20 +0100 +Subject: ovpn: tcp - use cached peer pointer in ovpn_tcp_close() + +From: David Carlier + +[ Upstream commit 775d8d7ad02aa345e1588424a6a8b9ae49fb9012 ] + +ovpn_tcp_close() loads the ovpn_socket via rcu_dereference_sk_user_data() +under rcu_read_lock(), takes a reference on sock->peer, caches the peer +pointer in a local, and drops the read lock. It then passes sock->peer +(rather than the cached local) to ovpn_peer_del(), re-dereferencing the +ovpn_socket after the RCU read section has ended. + +Unlike ovpn_tcp_sendmsg(), which uses the same "load under RCU, use +after unlock" pattern but is protected by lock_sock() held across the +function, ovpn_tcp_close() runs without the socket lock: inet_release() +invokes sk_prot->close() without taking lock_sock first. + +ovpn_socket_release() can therefore complete its kref_put -> detach -> +synchronize_rcu -> kfree(sock) sequence concurrently, in the window +after ovpn_tcp_close() drops rcu_read_lock() but before it dereferences +sock->peer. The synchronize_rcu() in ovpn_socket_release() protects +readers that use the dereferenced pointer inside the RCU read section, +not those that escape the pointer to a local and use it afterwards. + +A reproducer follows the pattern of commit 94560267d6c4 ("ovpn: tcp - +don't deref NULL sk_socket member after tcp_close()"): trigger a peer +removal (keepalive expiration or netlink OVPN_CMD_DEL_PEER) at the same +moment userspace closes the TCP fd. That commit fixed the detach-side +of the same race window; this one fixes the close-side at a different +victim. + +Tighten the entry block to read sock->peer exactly once into the cached +peer local, and route all subsequent uses (the hold check, the +ovpn_peer_del() call, and the prot->close() invocation) through that +local. sock->peer is only ever written once in ovpn_socket_new() under +lock_sock(), before rcu_assign_sk_user_data() publishes the ovpn_socket, +and is never reassigned afterwards - but the previous multi-read pattern +made that invariant implicit rather than explicit. The same multi-read +shape exists in ovpn_tcp_recvmsg(), ovpn_tcp_sendmsg(), +ovpn_tcp_data_ready() and ovpn_tcp_write_space(); those will be cleaned +up via a dedicated helper in a follow-up net-next series. + +Fixes: 11851cbd60ea ("ovpn: implement TCP transport") +Reviewed-by: Sabrina Dubroca +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: David Carlier +Signed-off-by: Antonio Quartulli +Signed-off-by: Sasha Levin +--- + drivers/net/ovpn/tcp.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c +index 5499c1572f3e2..5f345ae7d59d2 100644 +--- a/drivers/net/ovpn/tcp.c ++++ b/drivers/net/ovpn/tcp.c +@@ -581,14 +581,19 @@ static void ovpn_tcp_close(struct sock *sk, long timeout) + + rcu_read_lock(); + sock = rcu_dereference_sk_user_data(sk); +- if (!sock || !sock->peer || !ovpn_peer_hold(sock->peer)) { ++ if (!sock) { + rcu_read_unlock(); + return; + } ++ + peer = sock->peer; ++ if (!peer || !ovpn_peer_hold(peer)) { ++ rcu_read_unlock(); ++ return; ++ } + rcu_read_unlock(); + +- ovpn_peer_del(sock->peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); ++ ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); + peer->tcp.sk_cb.prot->close(sk, timeout); + ovpn_peer_put(peer); + } +-- +2.53.0 + diff --git a/queue-7.0/pds_core-ensure-null-termination-for-firmware-versio.patch b/queue-7.0/pds_core-ensure-null-termination-for-firmware-versio.patch new file mode 100644 index 0000000000..6b388f8210 --- /dev/null +++ b/queue-7.0/pds_core-ensure-null-termination-for-firmware-versio.patch @@ -0,0 +1,47 @@ +From 8368069b0be9c9e19753b54e46e742a85d93af5d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 20:58:42 +0000 +Subject: pds_core: ensure null-termination for firmware version strings + +From: Nikhil P. Rao + +[ Upstream commit 3d4432d34c1992701289cbe12df9fd024f315998 ] + +The driver passes fw_version directly to devlink_info_version_stored_put() +without ensuring null-termination. While current firmware null-terminates +these strings, the driver should not rely on this behavior. Add explicit +null-termination to prevent potential issues if firmware behavior changes. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c +index b576be626a294..3f0e56b951bf0 100644 +--- a/drivers/net/ethernet/amd/pds_core/devlink.c ++++ b/drivers/net/ethernet/amd/pds_core/devlink.c +@@ -122,12 +122,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + + listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); + for (i = 0; i < listlen; i++) { ++ char *fw_ver = fw_list.fw_names[i].fw_version; ++ + if (i < ARRAY_SIZE(fw_slotnames)) + strscpy(buf, fw_slotnames[i], sizeof(buf)); + else + snprintf(buf, sizeof(buf), "fw.slot_%d", i); +- err = devlink_info_version_stored_put(req, buf, +- fw_list.fw_names[i].fw_version); ++ fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0'; ++ err = devlink_info_version_stored_put(req, buf, fw_ver); + if (err) + return err; + } +-- +2.53.0 + diff --git a/queue-7.0/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch b/queue-7.0/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch new file mode 100644 index 0000000000..28622d29e9 --- /dev/null +++ b/queue-7.0/pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch @@ -0,0 +1,50 @@ +From 35dcf64a9b72e4ee96d62a49ff47b6816c5102ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:07 +0000 +Subject: pds_core: fix debugfs_lookup dentry leak and error handling + +From: Nikhil P. Rao + +[ Upstream commit dc416e32baaeb620b9809e9e25fc7b30889686e9 ] + +debugfs_lookup() returns a dentry with an elevated reference count that +must be released with dput(). The current code discards the returned +dentry without calling dput(), causing a reference leak on every +firmware reset recovery. + +Additionally, when CONFIG_DEBUG_FS is disabled, debugfs_lookup() +returns ERR_PTR(-ENODEV), not NULL. The current check passes for error +pointers and would call dput() on an invalid pointer, causing a crash. + +Fixes: bc90fbe0c318 ("pds_core: Rework teardown/setup flow to be more common") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/debugfs.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c +index 04c5e3abd8d70..810a0cd9bcac8 100644 +--- a/drivers/net/ethernet/amd/pds_core/debugfs.c ++++ b/drivers/net/ethernet/amd/pds_core/debugfs.c +@@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity); + + void pdsc_debugfs_add_ident(struct pdsc *pdsc) + { ++ struct dentry *dentry; ++ + /* This file will already exist in the reset flow */ +- if (debugfs_lookup("identity", pdsc->dentry)) ++ dentry = debugfs_lookup("identity", pdsc->dentry); ++ if (!IS_ERR_OR_NULL(dentry)) { ++ dput(dentry); + return; ++ } + + debugfs_create_file("identity", 0400, pdsc->dentry, + pdsc, &identity_fops); +-- +2.53.0 + diff --git a/queue-7.0/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch b/queue-7.0/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch new file mode 100644 index 0000000000..af8aeea6e8 --- /dev/null +++ b/queue-7.0/pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch @@ -0,0 +1,62 @@ +From 63a6c530859c51f9df7a10d8c6f2f9f4834171f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 21:29:05 +0000 +Subject: pds_core: fix error handling in pdsc_devcmd_wait + +From: Nikhil P. Rao + +[ Upstream commit 0e46b6635b03d29807f810c3b415c4755a3f958d ] + +Fix two cases where pdsc_devcmd_wait() returns stale success from +the completion register instead of an error: + +1. FW crash: If firmware stops running, the wait loop breaks early with + running=false. The condition "if ((!done || timeout) && running)" is + false, so error handling is bypassed and stale status is returned. + Check !running first and return -ENXIO. + +2. Timeout: If a command times out, err is set to -ETIMEDOUT but then + overwritten by pdsc_err_to_errno(status) which reads stale status. + Return -ETIMEDOUT immediately after cleaning up. + +Both errors now propagate to pdsc_devcmd_locked() which queues +health_work for recovery. + +Fixes: 45d76f492938 ("pds_core: set up device and adminq") +Signed-off-by: Nikhil P. Rao +Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/amd/pds_core/dev.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c +index 2e1d0d01d03af..bded6b33289ce 100644 +--- a/drivers/net/ethernet/amd/pds_core/dev.c ++++ b/drivers/net/ethernet/amd/pds_core/dev.c +@@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds) + dev_dbg(dev, "DEVCMD %d %s after %ld secs\n", + opcode, pdsc_devcmd_str(opcode), duration / HZ); + +- if ((!done || timeout) && running) { ++ if (!running) { ++ dev_err(dev, "DEVCMD %d %s fw not running\n", ++ opcode, pdsc_devcmd_str(opcode)); ++ pdsc_devcmd_clean(pdsc); ++ return -ENXIO; ++ } ++ ++ if (!done || timeout) { + dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n", + opcode, pdsc_devcmd_str(opcode), done, timeout, + max_seconds); +- err = -ETIMEDOUT; + pdsc_devcmd_clean(pdsc); ++ return -ETIMEDOUT; + } + + status = pdsc_devcmd_status(pdsc); +-- +2.53.0 + diff --git a/queue-7.0/phy-apple-atc-fix-typec-switch-mux-leak-on-unbind.patch b/queue-7.0/phy-apple-atc-fix-typec-switch-mux-leak-on-unbind.patch new file mode 100644 index 0000000000..757faedaff --- /dev/null +++ b/queue-7.0/phy-apple-atc-fix-typec-switch-mux-leak-on-unbind.patch @@ -0,0 +1,146 @@ +From bfc0f86d5128ae518f3bb64a3dc6bf6441a6b556 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 21:19:58 +0100 +Subject: phy: apple: atc: Fix typec switch/mux leak on unbind + +From: David Carlier + +[ Upstream commit 1854082fe0ddb81bc93d1f8e8a00554217fd09d1 ] + +atcphy_probe_switch() and atcphy_probe_mux() discard the pointers +returned by typec_switch_register() and typec_mux_register(). The +platform driver has no .remove callback, so when the driver unbinds +(e.g. via sysfs unbind) neither typec_switch_unregister() nor +typec_mux_unregister() is called. The framework reference taken in +typec_switch_register() (device_initialize() + device_add() in +drivers/usb/typec/mux.c) is therefore never dropped and the +typec_switch_dev / typec_mux_dev objects stay live forever, with +their sysfs entries under the typec_mux class also left behind. A +subsequent rebind cannot recreate them with the same fwnode-derived +name. + +Save the registered handles and unregister them through +devm_add_action_or_reset() so framework registration is torn down +in step with the driver's other devm-managed state. While here, +drop struct apple_atcphy::sw and ::mux: they were declared with the +consumer-side types (typec_switch *, typec_mux *) instead of the +provider-side types and were never assigned. + +Scope of the fix +================ +This patch fixes the registration leak only. It does not close the +use-after-free window that arises when a consumer that obtained a +reference via fwnode_typec_switch_get() / fwnode_typec_mux_get() +outlives the provider unbind: such consumers keep the underlying +typec_switch_dev / typec_mux_dev alive past device_unregister(), +and a later typec_switch_set() / typec_mux_set() still invokes the +registered atcphy_sw_set() / atcphy_mux_set(), which dereferences +the freed apple_atcphy through typec_{switch,mux}_get_drvdata(). + +On Apple Silicon the relevant consumers are the typec port and the +cd321x controller registered by drivers/usb/typec/tipd/core.c. +Cable plug / orientation events and alt-mode transitions trigger +the .set callbacks via: + + tps6598x_interrupt() drivers/usb/typec/tipd/core.c + tps6598x_handle_plug_event() + tps6598x_connect()/_disconnect() + typec_set_orientation() drivers/usb/typec/class.c + typec_switch_set(port->sw) drivers/usb/typec/mux.c + atcphy_sw_set() drivers/phy/apple/atc.c + + cd321x_update_work() drivers/usb/typec/tipd/core.c + cd321x_typec_update_mode() + typec_mux_set(cd321x->mux) drivers/usb/typec/mux.c + atcphy_mux_set() drivers/phy/apple/atc.c + +Closing that window requires framework support for invalidating +consumer-held references on provider unbind. The same +consumer-survives-provider pattern has been discussed for the PHY +framework [1] and is out of scope here. + +[1] https://lore.kernel.org/linux-phy/aZejMSJ9qqRWb2pX@google.com/ + +Fixes: 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY") +Signed-off-by: David Carlier +Reviewed-by: Vladimir Oltean +Tested-by: Joshua Peisach +Link: https://lkml.kernel.org/r/6ec1ed08328340db42655287afd5fa4067316b11.camel@perches.com +Link: https://patch.msgid.link/20260508201958.30060-1-devnexen@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/apple/atc.c | 27 ++++++++++++++++++++++----- + 1 file changed, 22 insertions(+), 5 deletions(-) + +diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c +index 64d0c3dba1cbb..4f0585818fa7a 100644 +--- a/drivers/phy/apple/atc.c ++++ b/drivers/phy/apple/atc.c +@@ -628,9 +628,6 @@ struct apple_atcphy { + + struct reset_controller_dev rcdev; + +- struct typec_switch *sw; +- struct typec_mux *mux; +- + struct mutex lock; + }; + +@@ -2066,15 +2063,25 @@ static int atcphy_sw_set(struct typec_switch_dev *sw, enum typec_orientation ori + return 0; + } + ++static void atcphy_typec_switch_unregister(void *data) ++{ ++ typec_switch_unregister(data); ++} ++ + static int atcphy_probe_switch(struct apple_atcphy *atcphy) + { ++ struct typec_switch_dev *sw; + struct typec_switch_desc sw_desc = { + .drvdata = atcphy, + .fwnode = atcphy->dev->fwnode, + .set = atcphy_sw_set, + }; + +- return PTR_ERR_OR_ZERO(typec_switch_register(atcphy->dev, &sw_desc)); ++ sw = typec_switch_register(atcphy->dev, &sw_desc); ++ if (IS_ERR(sw)) ++ return PTR_ERR(sw); ++ ++ return devm_add_action_or_reset(atcphy->dev, atcphy_typec_switch_unregister, sw); + } + + static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) +@@ -2146,15 +2153,25 @@ static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *sta + return atcphy_configure(atcphy, target_mode); + } + ++static void atcphy_typec_mux_unregister(void *data) ++{ ++ typec_mux_unregister(data); ++} ++ + static int atcphy_probe_mux(struct apple_atcphy *atcphy) + { ++ struct typec_mux_dev *mux; + struct typec_mux_desc mux_desc = { + .drvdata = atcphy, + .fwnode = atcphy->dev->fwnode, + .set = atcphy_mux_set, + }; + +- return PTR_ERR_OR_ZERO(typec_mux_register(atcphy->dev, &mux_desc)); ++ mux = typec_mux_register(atcphy->dev, &mux_desc); ++ if (IS_ERR(mux)) ++ return PTR_ERR(mux); ++ ++ return devm_add_action_or_reset(atcphy->dev, atcphy_typec_mux_unregister, mux); + } + + static int atcphy_load_tunables(struct apple_atcphy *atcphy) +-- +2.53.0 + diff --git a/queue-7.0/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch b/queue-7.0/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch new file mode 100644 index 0000000000..89ab1c425d --- /dev/null +++ b/queue-7.0/phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch @@ -0,0 +1,51 @@ +From aab14623f2913cfd429f5181583298e5e739b310 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Mar 2026 15:42:32 +0100 +Subject: phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Gabor Juhos + +[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] + +The mvebu_a3700_utmi_phy_power_off() function tries to modify the +USB2_PHY_CTRL register by using the IO address of the PHY IP block along +with the readl/writel IO accessors. However, the register exist in the +USB miscellaneous register space, and as such it must be accessed via +regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. + +Change the code to use regmap_update_bits() for modífying the register +to fix this. + +Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") +Signed-off-by: Gabor Juhos +Reviewed-by: Miquel Raynal +Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +index 04f4fb4bed702..f882bc57649c7 100644 +--- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ +- reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); +- reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); +- writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); ++ regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), ++ RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); + + /* Power down OTG module */ + if (usb32) { +-- +2.53.0 + diff --git a/queue-7.0/phy-qcom-qmp-usbc-fix-out-of-bounds-array-access-in-.patch b/queue-7.0/phy-qcom-qmp-usbc-fix-out-of-bounds-array-access-in-.patch new file mode 100644 index 0000000000..36392bd0ff --- /dev/null +++ b/queue-7.0/phy-qcom-qmp-usbc-fix-out-of-bounds-array-access-in-.patch @@ -0,0 +1,42 @@ +From 452474a7b864540ca5a565ca32fd6d97d8671613 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Feb 2026 20:15:01 +0800 +Subject: phy: qcom: qmp-usbc: Fix out-of-bounds array access in dp swing + config + +From: Xiangxu Yin + +[ Upstream commit ea17fc4d7dc2ba6459b1a318962960520201baf1 ] + +swing_tbl and pre_emphasis_tbl are 4x4 arrays (valid indices 0-3), but +the boundary check uses "> 4" instead of ">= 4", allowing index 4 to +cause an out-of-bounds access. + +Reported-by: Dan Carpenter +Fixes: 81791c45c8e0 ("phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY config and DP mode support") +Signed-off-by: Xiangxu Yin +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20260227-master-v1-1-8d91b9407fdb@oss.qualcomm.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +index 14feb77789b3e..0dd7000614f44 100644 +--- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c ++++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +@@ -794,7 +794,7 @@ static int qmp_v2_configure_dp_swing(struct qmp_usbc *qmp) + p_level = max(p_level, dp_opts->pre[i]); + } + +- if (v_level > 4 || p_level > 4) { ++ if (v_level >= 4 || p_level >= 4) { + dev_err(qmp->dev, "Invalid v(%d) | p(%d) level)\n", + v_level, p_level); + return -EINVAL; +-- +2.53.0 + diff --git a/queue-7.0/phy-spacemit-remove-incorrect-clk_disable-in-spacemi.patch b/queue-7.0/phy-spacemit-remove-incorrect-clk_disable-in-spacemi.patch new file mode 100644 index 0000000000..48a3c19698 --- /dev/null +++ b/queue-7.0/phy-spacemit-remove-incorrect-clk_disable-in-spacemi.patch @@ -0,0 +1,41 @@ +From 6bbc833a4bf8376191c4a948fa0e1992be6feba9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Mar 2026 00:23:58 +0800 +Subject: phy: spacemit: Remove incorrect clk_disable() in + spacemit_usb2phy_init() + +From: Felix Gu + +[ Upstream commit a4058c09dd6e28ec33316fd6eb45ddae4cab1f31 ] + +When clk_enable() fails, the clock was never enabled. Calling +clk_disable() in this error path is incorrect. + +Remove the spurious clk_disable() call from the error handling +in spacemit_usb2phy_init(). + +Fixes: fe4bc1a08638 ("phy: spacemit: support K1 USB2.0 PHY controller") +Signed-off-by: Felix Gu +Reviewed-by: Ze Huang +Link: https://patch.msgid.link/20260326-k1-usb3-v1-1-0c2b6adf5185@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/spacemit/phy-k1-usb2.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c +index 9215d0b223b2d..e8c1e26428a91 100644 +--- a/drivers/phy/spacemit/phy-k1-usb2.c ++++ b/drivers/phy/spacemit/phy-k1-usb2.c +@@ -97,7 +97,6 @@ static int spacemit_usb2phy_init(struct phy *phy) + ret = clk_enable(sphy->clk); + if (ret) { + dev_err(&phy->dev, "failed to enable clock\n"); +- clk_disable(sphy->clk); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-mediatek-moore-implement-gpio_chip-get_direc.patch b/queue-7.0/pinctrl-mediatek-moore-implement-gpio_chip-get_direc.patch new file mode 100644 index 0000000000..36bb9b113f --- /dev/null +++ b/queue-7.0/pinctrl-mediatek-moore-implement-gpio_chip-get_direc.patch @@ -0,0 +1,65 @@ +From c3fec687840321fce5722181c33f002aa1c70dbc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 09:09:35 +0200 +Subject: pinctrl: mediatek: moore: implement gpio_chip::get_direction() + +From: Bartosz Golaszewski + +[ Upstream commit b560d414239232c6ed7205d3795d3f588034d69b ] + +If the gpio_chip::get_direction() callback is not implemented by the GPIO +controller driver, GPIOLIB emits a warning. + +Implement get_direction() for the GPIO part of pinctrl-moore. + +Fixes: 471e998c0e31 ("gpiolib: remove redundant callback check") +Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") +Reported-by: Frank Wunderlich +Closes: https://lore.kernel.org/all/20260409132724.126258-1-linux@fw-web.de/ +Signed-off-by: Bartosz Golaszewski +Tested-By: Frank Wunderlich +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/mediatek/pinctrl-moore.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/drivers/pinctrl/mediatek/pinctrl-moore.c b/drivers/pinctrl/mediatek/pinctrl-moore.c +index 70f608347a5f6..071ba849e5322 100644 +--- a/drivers/pinctrl/mediatek/pinctrl-moore.c ++++ b/drivers/pinctrl/mediatek/pinctrl-moore.c +@@ -520,6 +520,23 @@ static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, + return pinctrl_gpio_direction_output(chip, gpio); + } + ++static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) ++{ ++ struct mtk_pinctrl *hw = gpiochip_get_data(chip); ++ const struct mtk_pin_desc *desc; ++ int ret, dir; ++ ++ desc = (const struct mtk_pin_desc *)&hw->soc->pins[offset]; ++ if (!desc->name) ++ return -ENOTSUPP; ++ ++ ret = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_DIR, &dir); ++ if (ret) ++ return ret; ++ ++ return dir ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; ++} ++ + static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) + { + struct mtk_pinctrl *hw = gpiochip_get_data(chip); +@@ -566,6 +583,7 @@ static int mtk_build_gpiochip(struct mtk_pinctrl *hw) + chip->parent = hw->dev; + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; ++ chip->get_direction = mtk_gpio_get_direction; + chip->direction_input = pinctrl_gpio_direction_input; + chip->direction_output = mtk_gpio_direction_output; + chip->get = mtk_gpio_get; +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch b/queue-7.0/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch new file mode 100644 index 0000000000..e5461b5522 --- /dev/null +++ b/queue-7.0/pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch @@ -0,0 +1,60 @@ +From e8f958dcf3278ae90ca9a2c449f5f551fdc041d8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Apr 2026 11:44:13 +0000 +Subject: pinctrl: meson: amlogic-a4: fix deadlock issue + +From: Xianwei Zhao + +[ Upstream commit e72ce029810390eb987a036fb2c8a5da9a23b685 ] + +Accessing the pinconf-pins sysfs node may deadlock. + +pinconf_pins_show() holds pctldev->mutex, and the platform driver +calls pinctrl_find_gpio_range_from_pin(), which tries to acquire +the same mutex again, leading to a deadlock. + +Use pinctrl_find_gpio_range_from_pin_nolock() to fix this issue. + +Fixes: 6e9be3abb78c ("pinctrl: Add driver support for Amlogic SoCs") +Signed-off-by: Xianwei Zhao +Reviewed-by: Neil Armstrong +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +index e2293a872dcb7..35d27626a336b 100644 +--- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c ++++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +@@ -292,7 +292,7 @@ static int aml_calc_reg_and_bit(struct pinctrl_gpio_range *range, + static int aml_pinconf_get_pull(struct aml_pinctrl *info, unsigned int pin) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit, val; + int ret, conf; +@@ -326,7 +326,7 @@ static int aml_pinconf_get_drive_strength(struct aml_pinctrl *info, + u16 *drive_strength_ua) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit; + unsigned int val; +@@ -365,7 +365,7 @@ static int aml_pinconf_get_gpio_bit(struct aml_pinctrl *info, + unsigned int reg_type) + { + struct pinctrl_gpio_range *range = +- pinctrl_find_gpio_range_from_pin(info->pctl, pin); ++ pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); + struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); + unsigned int reg, bit, val; + int ret; +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch b/queue-7.0/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch new file mode 100644 index 0000000000..fe29d788f9 --- /dev/null +++ b/queue-7.0/pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch @@ -0,0 +1,45 @@ +From 61f53705cbf2f63f606aeb8e7b2db9989de58654 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Apr 2026 16:55:24 +0530 +Subject: pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615 + +From: Maulik Shah + +[ Upstream commit 9d69033ad967b6e09b1e5b30d1a32c6c4876465d ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but qcs615 do not +support i3c. GPIOs 39,51,88 and 89 are also connected to different PDC +pin to support non-ibi wakeup. Update the wakeirq map to reflect same. + +Fixes: b698f36a9d40 ("pinctrl: qcom: add the tlmm driver for QCS615 platform") +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Reviewed-by: Konrad Dybcio +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-qcs615.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c +index f1c827ddbfbfa..4d474c312c10b 100644 +--- a/drivers/pinctrl/qcom/pinctrl-qcs615.c ++++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c +@@ -1043,11 +1043,11 @@ static const struct msm_pingroup qcs615_groups[] = { + static const struct msm_gpio_wakeirq_map qcs615_pdc_map[] = { + { 1, 45 }, { 3, 31 }, { 7, 55 }, { 9, 110 }, { 11, 34 }, + { 13, 33 }, { 14, 35 }, { 17, 46 }, { 19, 48 }, { 21, 83 }, +- { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 125 }, { 41, 47 }, +- { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 123 }, { 55, 56 }, ++ { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 118 }, { 41, 47 }, ++ { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 116 }, { 55, 56 }, + { 56, 57 }, { 57, 58 }, { 60, 60 }, { 71, 54 }, { 80, 73 }, + { 81, 64 }, { 82, 50 }, { 83, 65 }, { 84, 92 }, { 85, 99 }, +- { 86, 67 }, { 87, 84 }, { 88, 124 }, { 89, 122 }, { 90, 69 }, ++ { 86, 67 }, { 87, 84 }, { 88, 117 }, { 89, 115 }, { 90, 69 }, + { 92, 88 }, { 93, 75 }, { 94, 91 }, { 95, 72 }, { 96, 82 }, + { 97, 74 }, { 98, 95 }, { 99, 94 }, { 100, 100 }, { 101, 40 }, + { 102, 93 }, { 103, 77 }, { 104, 78 }, { 105, 96 }, { 107, 97 }, +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch b/queue-7.0/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch new file mode 100644 index 0000000000..50f2825960 --- /dev/null +++ b/queue-7.0/pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch @@ -0,0 +1,56 @@ +From 6e4bd75ba189ac8dff991f365dd50b7e3cf34b3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 17:44:58 +0530 +Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for + sm8150 + +From: Maulik Shah + +[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] + +PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not +support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC +pin and already reflected in the wake irq map. + +Remove the unsupported wakeup interrupts from the map. + +Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") +Reviewed-by: Konrad Dybcio +Signed-off-by: Maulik Shah +Signed-off-by: Navya Malempati +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c +index ad861cd66958c..e4c561a9c50ae 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c ++++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c +@@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { + { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, + { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, + { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, +- { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, +- { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, ++ { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, ++ { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, + { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, + { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, + { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, +- { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, ++ { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, + { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, + { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, + { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, + { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, +- { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, ++ { 144, 115 }, { 147, 102 }, { 150, 107 }, + { 152, 108 }, { 153, 109 } + }; + +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch b/queue-7.0/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch new file mode 100644 index 0000000000..fc2d46266b --- /dev/null +++ b/queue-7.0/pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch @@ -0,0 +1,65 @@ +From b54f0cbc56bd6fbe3990365d6eb0b765cf99e139 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Apr 2026 15:52:34 +0200 +Subject: pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function + +From: Til Kaiser + +[ Upstream commit b51d33ea8a164bb5f0eec8ad817fa9730ac2b577 ] + +The qcom pinctrl core supports marking functions that represent GPIO mode +via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject +GPIO requests for pins that are muxed to the GPIO function. + +ipq4019 still describes its gpio function with QCA_PIN_FUNCTION(gpio), +so it is not treated as a GPIO pin function. As a result, GPIO consumers +can still conflict with pinctrl states that select the "gpio" function. + +Add a QCA_GPIO_PIN_FUNCTION() helper and use it for the ipq4019 gpio +function, matching how the msm-based qcom drivers handle this. + +This allows ipq4019 to keep the GPIO-related pin configuration in DTS +without tripping over strict pinmux ownership checks. + +Fixes: cc85cb96e2e4 ("pinctrl: qcom: make the pinmuxing strict") +Signed-off-by: Til Kaiser +Reviewed-by: Dmitry Baryshkov +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-ipq4019.c | 2 +- + drivers/pinctrl/qcom/pinctrl-msm.h | 5 +++++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c +index 6ede3149b6e17..07df812fb7282 100644 +--- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c ++++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c +@@ -480,7 +480,7 @@ static const struct pinfunction ipq4019_functions[] = { + QCA_PIN_FUNCTION(blsp_uart0), + QCA_PIN_FUNCTION(blsp_uart1), + QCA_PIN_FUNCTION(chip_rst), +- QCA_PIN_FUNCTION(gpio), ++ QCA_GPIO_PIN_FUNCTION(gpio), + QCA_PIN_FUNCTION(i2s_rx), + QCA_PIN_FUNCTION(i2s_spdif_in), + QCA_PIN_FUNCTION(i2s_spdif_out), +diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h +index 4625fa5320a95..120217012a9f6 100644 +--- a/drivers/pinctrl/qcom/pinctrl-msm.h ++++ b/drivers/pinctrl/qcom/pinctrl-msm.h +@@ -39,6 +39,11 @@ struct pinctrl_pin_desc; + fname##_groups, \ + ARRAY_SIZE(fname##_groups)) + ++#define QCA_GPIO_PIN_FUNCTION(fname) \ ++ [qca_mux_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \ ++ fname##_groups, \ ++ ARRAY_SIZE(fname##_groups)) ++ + /** + * struct msm_pingroup - Qualcomm pingroup definition + * @grp: Generic data of the pin group (name and pins) +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch b/queue-7.0/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch new file mode 100644 index 0000000000..58d31b823b --- /dev/null +++ b/queue-7.0/pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch @@ -0,0 +1,49 @@ +From 724e42b37ddf1617dda89b4bfd51e78fed43cf3d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 Mar 2026 09:05:45 +0000 +Subject: pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high + pins during suspend/resume +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Biju Das + +[ Upstream commit 6dba9b7268cc50166bce47608670192fd874e363 ] + +When saving/restoring pull-up/down register state during suspend/resume, +the second PUPD register access was incorrectly using the same base offset +as the first, effectively reading/writing the same register twice instead +of the adjacent one. + +Add the correct + 4 byte offset to the second RZG2L_PCTRL_REG_ACCESS32 +call so that pupd[1][port] is properly saved and restored from the next +32-bit register in the PUPD register pair, covering pins 4–7 of ports +with 4 or more pins. + +Fixes: b2bd65fbb617 ("pinctrl: renesas: rzg2l: Add suspend/resume support for pull up/down") +Signed-off-by: Biju Das +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +index 55e35f63343c7..36c3995bac836 100644 +--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c ++++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +@@ -3050,7 +3050,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), + cache->pupd[0][port]); + if (pincnt >= 4) { +- RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off) + 4, + cache->pupd[1][port]); + } + } +-- +2.53.0 + diff --git a/queue-7.0/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch b/queue-7.0/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch new file mode 100644 index 0000000000..07e249bef2 --- /dev/null +++ b/queue-7.0/pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch @@ -0,0 +1,87 @@ +From c2cb5e0f7a709c85b030283185567603eabbb739 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Apr 2026 19:24:51 +0100 +Subject: pinctrl: renesas: rzg2l: Fix SMT register cache handling + +From: Lad Prabhakar + +[ Upstream commit c88ab9407986836820848128ce1f90f2fa49da95 ] + +Store SMT register cache per bank instead of using a single array. + +On RZ/V2H(P), RZ/V2N, and RZ/G3E, the SMT register is split across two +32-bit registers: bits 0/8/16/24 control pins 0-3, while pins 4-7 are +controlled by the corresponding bits in the next register. The previous +implementation cached only a single SMT register, leading to incomplete +save/restore of SMT state. + +Convert cache->smt to a per-bank array and allocate storage for both +halves. Update suspend/resume handling to save and restore both SMT +registers when present. + +Fixes: 837afa592c623 ("pinctrl: renesas: rzg2l: Add suspend/resume support for Schmitt control registers") +Signed-off-by: Lad Prabhakar +Reviewed-by: Geert Uytterhoeven +Link: https://patch.msgid.link/20260413182456.811543-2-prabhakar.mahadev-lad.rj@bp.renesas.com +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/renesas/pinctrl-rzg2l.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +index 36c3995bac836..99008ec3deb03 100644 +--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c ++++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c +@@ -335,7 +335,7 @@ struct rzg2l_pinctrl_reg_cache { + u32 *iolh[2]; + u32 *ien[2]; + u32 *pupd[2]; +- u32 *smt; ++ u32 *smt[2]; + u8 sd_ch[2]; + u8 eth_poc[2]; + u8 oen; +@@ -2738,10 +2738,6 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) + if (!cache->pfc) + return -ENOMEM; + +- cache->smt = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt), GFP_KERNEL); +- if (!cache->smt) +- return -ENOMEM; +- + for (u8 i = 0; i < 2; i++) { + u32 n_dedicated_pins = pctrl->data->n_dedicated_pins; + +@@ -2760,6 +2756,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) + if (!cache->pupd[i]) + return -ENOMEM; + ++ cache->smt[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt[i]), ++ GFP_KERNEL); ++ if (!cache->smt[i]) ++ return -ENOMEM; ++ + /* Allocate dedicated cache. */ + dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->iolh[i]), +@@ -3067,8 +3068,14 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen + } + } + +- if (has_smt) +- RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), cache->smt[port]); ++ if (has_smt) { ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), ++ cache->smt[0][port]); ++ if (pincnt >= 4) { ++ RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off) + 4, ++ cache->smt[1][port]); ++ } ++ } + } + } + +-- +2.53.0 + diff --git a/queue-7.0/platform-surface-aggregator_registry-omit-battery-ac.patch b/queue-7.0/platform-surface-aggregator_registry-omit-battery-ac.patch new file mode 100644 index 0000000000..956fef5cb2 --- /dev/null +++ b/queue-7.0/platform-surface-aggregator_registry-omit-battery-ac.patch @@ -0,0 +1,47 @@ +From 7fe9f989d855c9de8a4e7a2ab12bc8e0f34c2482 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Apr 2026 15:43:47 +1200 +Subject: platform/surface: aggregator_registry: omit battery & AC nodes on + Surface Laptop 7 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Oliver White + +[ Upstream commit 0488073a6c84571dd3cffe581a4a73a5fceb099d ] + +Surface Laptop 7 exposes battery and AC status via Qualcomm PMIC GLINK +qcom_battmgr. Registering the standard SSAM battery and AC client +devices on this platform causes duplicate power-supply devices to +appear. + +Drop the SSAM battery and AC nodes from the Surface Laptop 7 registry +group so that only the qcom_battmgr power supplies are instantiated. + +Fixes: b27622f13172 ("platform/surface: Add OF support") +Signed-off-by: Oliver White +Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/surface/surface_aggregator_registry.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c +index 0599d5adf02e6..f0881edfb6161 100644 +--- a/drivers/platform/surface/surface_aggregator_registry.c ++++ b/drivers/platform/surface/surface_aggregator_registry.c +@@ -295,8 +295,6 @@ static const struct software_node *ssam_node_group_sl6[] = { + /* Devices for Surface Laptop 7. */ + static const struct software_node *ssam_node_group_sl7[] = { + &ssam_node_root, +- &ssam_node_bat_ac, +- &ssam_node_bat_main, + &ssam_node_tmp_perf_profile_with_fan, + &ssam_node_fan_speed, + &ssam_node_hid_sam_keyboard, +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-adv_swbutton-check-acpi_handle-against-.patch b/queue-7.0/platform-x86-adv_swbutton-check-acpi_handle-against-.patch new file mode 100644 index 0000000000..60b326b47b --- /dev/null +++ b/queue-7.0/platform-x86-adv_swbutton-check-acpi_handle-against-.patch @@ -0,0 +1,54 @@ +From bdeb9a72eb50b75f0618f1545a24ce9ab723b2c6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:11:49 +0200 +Subject: platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 adv_swbutton driver. + +Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/adv_swbutton.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c +index 6fa60f3fc53c0..8f7a26e6de81d 100644 +--- a/drivers/platform/x86/adv_swbutton.c ++++ b/drivers/platform/x86/adv_swbutton.c +@@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) + { + struct adv_swbutton *button; + struct input_dev *input; +- acpi_handle handle = ACPI_HANDLE(&device->dev); ++ acpi_handle handle; + acpi_status status; + int error; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-asus-armoury-fix-mini-led-mode-get-set-.patch b/queue-7.0/platform-x86-asus-armoury-fix-mini-led-mode-get-set-.patch new file mode 100644 index 0000000000..c3d9bc8546 --- /dev/null +++ b/queue-7.0/platform-x86-asus-armoury-fix-mini-led-mode-get-set-.patch @@ -0,0 +1,93 @@ +From b932b2f6089ad55a86f3336f6205bcbe895760dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 17 May 2026 18:30:11 +0000 +Subject: platform/x86: asus-armoury: fix mini-LED mode get/set on MODE2 + devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ahmed Yaseen + +[ Upstream commit d2d2e7c8fb37b27301ee5c8343b2f7037efc6ea6 ] + +The mini-LED current_value attribute does not work on devices that use +ASUS_WMI_DEVID_MINI_LED_MODE2 (2024 and newer models). + +Reading is broken: mini_led_mode_current_value_show() fetches the mode +from the device but then decodes a literal 0 instead of the value it +just read: + + mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, 0); + +So mode is always 0, and the attribute always reports the same thing +regardless of the real hardware state. + +Writing is broken too. The number a user writes is an index; the value +the firmware actually wants is looked up from that index in +mini_led_mode_map[]. mini_led_mode_current_value_store() skips that +lookup and passes the raw index straight to armoury_attr_uint_store(). +On 2024 devices the firmware numbers its modes differently from the +index, so some writes are rejected with -EINVAL and the rest send the +wrong mode to the hardware. + +Fix both paths: decode the value actually read from the device when +reading, and look up the firmware value before sending it when +writing. Older (MODE1) devices were unaffected because there the index +and the firmware value are the same. + +Fixes: f99eb098090e ("platform/x86: asus-armoury: move existing tunings to asus-armoury module") +Signed-off-by: Ahmed Yaseen +Reviewed-by: Denis Benato +Link: https://patch.msgid.link/20260517182957.11069-1-yaseen@ghoul.dev +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/asus-armoury.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c +index 5b0987ccc2702..495dc1e31d40e 100644 +--- a/drivers/platform/x86/asus-armoury.c ++++ b/drivers/platform/x86/asus-armoury.c +@@ -370,7 +370,7 @@ static ssize_t mini_led_mode_current_value_show(struct kobject *kobj, + if (err) + return err; + +- mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, 0); ++ mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, mode); + + for (i = 0; i < mini_led_mode_map_size; i++) + if (mode == mini_led_mode_map[i]) +@@ -386,6 +386,7 @@ static ssize_t mini_led_mode_current_value_store(struct kobject *kobj, + { + u32 *mini_led_mode_map; + size_t mini_led_mode_map_size; ++ char mapped_value[12]; + u32 mode; + int err; + +@@ -414,9 +415,16 @@ static ssize_t mini_led_mode_current_value_store(struct kobject *kobj, + return -ENODEV; + } + +- return armoury_attr_uint_store(kobj, attr, buf, count, +- 0, mini_led_mode_map[mode], +- NULL, asus_armoury.mini_led_dev_id); ++ /* ++ * armoury_attr_uint_store() parses and sends the value from the ++ * passed buffer; hand it the mapped firmware value so the device ++ * receives the translated mode instead of the raw index. ++ */ ++ snprintf(mapped_value, sizeof(mapped_value), "%u", mini_led_mode_map[mode]); ++ ++ return armoury_attr_uint_store(kobj, attr, mapped_value, count, 0, ++ mini_led_mode_map[mode], NULL, ++ asus_armoury.mini_led_dev_id); + } + + static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj, +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-hp_accel-check-acpi_companion-against-n.patch b/queue-7.0/platform-x86-hp_accel-check-acpi_companion-against-n.patch new file mode 100644 index 0000000000..378ff79cdc --- /dev/null +++ b/queue-7.0/platform-x86-hp_accel-check-acpi_companion-against-n.patch @@ -0,0 +1,48 @@ +From e71b5f42db042c0566e423548fd695e5f0b67bf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:12:40 +0200 +Subject: platform/x86: hp_accel: Check ACPI_COMPANION() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_COMPANION() check against NULL to the +platform/x86 hp_accel driver. + +Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/hp/hp_accel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c +index 10d5af18d6398..39b73dc473f1c 100644 +--- a/drivers/platform/x86/hp/hp_accel.c ++++ b/drivers/platform/x86/hp/hp_accel.c +@@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) + int ret; + + lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); ++ if (!lis3_dev.bus_priv) ++ return -ENODEV; ++ + lis3_dev.init = lis3lv02d_acpi_init; + lis3_dev.read = lis3lv02d_acpi_read; + lis3_dev.write = lis3lv02d_acpi_write; +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-intel-hid-check-acpi_handle-against-nul.patch b/queue-7.0/platform-x86-intel-hid-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..6c94d53f66 --- /dev/null +++ b/queue-7.0/platform-x86-intel-hid-check-acpi_handle-against-nul.patch @@ -0,0 +1,56 @@ +From 9f5b8618d0ecc03717ef33cc26acd517813a9691 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:13:28 +0200 +Subject: platform/x86: intel-hid: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-hid driver. + +Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index 2ddd8af8c1ce9..085093506dda9 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -688,12 +688,16 @@ static bool button_array_present(struct platform_device *device) + + static int intel_hid_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long mode, dummy; + struct intel_hid_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + intel_hid_init_dsm(handle); + + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch b/queue-7.0/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch new file mode 100644 index 0000000000..d2ebc62135 --- /dev/null +++ b/queue-7.0/platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch @@ -0,0 +1,56 @@ +From 64d31dec6cdcfffa7657d102d86ceb18ee8685e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:16:22 +0200 +Subject: platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel-vbtn driver. + +Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/vbtn.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c +index 9ca87e7075822..874023c38fd15 100644 +--- a/drivers/platform/x86/intel/vbtn.c ++++ b/drivers/platform/x86/intel/vbtn.c +@@ -275,12 +275,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) + + static int intel_vbtn_probe(struct platform_device *device) + { +- acpi_handle handle = ACPI_HANDLE(&device->dev); + bool dual_accel, has_buttons, has_switches; + struct intel_vbtn_priv *priv; ++ acpi_handle handle; + acpi_status status; + int err; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + dual_accel = dual_accel_detect(); + has_buttons = acpi_has_method(handle, "VBDL"); + has_switches = intel_vbtn_has_switches(handle, dual_accel); +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-intel_sar-check-acpi_handle-against-nul.patch b/queue-7.0/platform-x86-intel_sar-check-acpi_handle-against-nul.patch new file mode 100644 index 0000000000..907682f610 --- /dev/null +++ b/queue-7.0/platform-x86-intel_sar-check-acpi_handle-against-nul.patch @@ -0,0 +1,60 @@ +From 10ca759bbb9b1b04a8d5b738ec2aa186fd8045e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 17:15:32 +0200 +Subject: platform/x86: intel_sar: Check ACPI_HANDLE() against NULL +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Rafael J. Wysocki + +[ Upstream commit 2765f16c12af7c2533763e46b8113b727354012d ] + +Every platform driver can be forced to match a device that doesn't match +its list of device IDs because of device_match_driver_override(), so +platform drivers that rely on the existence of a device's ACPI companion +object need to verify its presence. + +Accordingly, add a requisite ACPI_HANDLE() check against NULL to the +platform/x86 intel_sar driver. + +Fixes: dcfbd31ef4bc ("platform/x86: BIOS SAR driver for Intel M.2 Modem") +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/14023870.uLZWGnKmhe@rafael.j.wysocki +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/int1092/intel_sar.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c +index 88822023a1490..849f7b415c1e5 100644 +--- a/drivers/platform/x86/intel/int1092/intel_sar.c ++++ b/drivers/platform/x86/intel/int1092/intel_sar.c +@@ -245,15 +245,20 @@ static void sar_get_data(int reg, struct wwan_sar_context *context) + static int sar_probe(struct platform_device *device) + { + struct wwan_sar_context *context; ++ acpi_handle handle; + int reg; + int result; + ++ handle = ACPI_HANDLE(&device->dev); ++ if (!handle) ++ return -ENODEV; ++ + context = kzalloc_obj(*context); + if (!context) + return -ENOMEM; + + context->sar_device = device; +- context->handle = ACPI_HANDLE(&device->dev); ++ context->handle = handle; + dev_set_drvdata(&device->dev, context); + + result = guid_parse(SAR_DSM_UUID, &context->guid); +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-uniwill-laptop-accept-charging-threshol.patch b/queue-7.0/platform-x86-uniwill-laptop-accept-charging-threshol.patch new file mode 100644 index 0000000000..a356bc0557 --- /dev/null +++ b/queue-7.0/platform-x86-uniwill-laptop-accept-charging-threshol.patch @@ -0,0 +1,54 @@ +From 73258e57e21a71552d14c4a8216c93fe2e6cec67 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 01:21:39 +0200 +Subject: platform/x86: uniwill-laptop: Accept charging threshold of 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Armin Wolf + +[ Upstream commit c16a4823cc60a32b891f7a148bb30c0f51d12cf4 ] + +The power supply sysfs ABI states that: + + Not all hardware is capable of setting this to an arbitrary + percentage. Drivers will round written values to the nearest + supported value. Reading back the value will show the actual + threshold set by the driver. + +The driver currently violates this ABI by rejecting a charging +threshold of 0. Fix this by clamping this value to 1. + +Fixes: d050479693bb ("platform/x86: Add Uniwill laptop driver") +Reviewed-by: Werner Sembach +Reviewed-by: Ilpo Järvinen +Signed-off-by: Armin Wolf +Link: https://patch.msgid.link/20260512232145.329260-3-W_Armin@gmx.de +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/uniwill/uniwill-acpi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c +index 4b491fe8bdea4..07951e01b43db 100644 +--- a/drivers/platform/x86/uniwill/uniwill-acpi.c ++++ b/drivers/platform/x86/uniwill/uniwill-acpi.c +@@ -1265,11 +1265,11 @@ static int uniwill_set_property(struct power_supply *psy, const struct power_sup + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: +- if (val->intval < 1 || val->intval > 100) ++ if (val->intval < 0 || val->intval > 100) + return -EINVAL; + + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, +- val->intval); ++ max(val->intval, 1)); + default: + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-uniwill-laptop-do-not-enable-the-chargi.patch b/queue-7.0/platform-x86-uniwill-laptop-do-not-enable-the-chargi.patch new file mode 100644 index 0000000000..65f914bab0 --- /dev/null +++ b/queue-7.0/platform-x86-uniwill-laptop-do-not-enable-the-chargi.patch @@ -0,0 +1,74 @@ +From 36a69fc1389c001e13d76ac5b0f51396e2c63dc7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 01:21:41 +0200 +Subject: platform/x86: uniwill-laptop: Do not enable the charging limit even + when forced +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Armin Wolf + +[ Upstream commit 26cbe119f99c86dcb4a0136d2bc73c0c716d80e4 ] + +It seems that on some older models (~2020) the battery charging limit +can permanently damage the battery. Prevent users from enabling this +feature thru the "force" module parameter to avoid causing permanent +hardware damage on such devices. + +Fixes: d050479693bb ("platform/x86: Add Uniwill laptop driver") +Link: https://www.reddit.com/r/XMG_gg/comments/ld9yyf/battery_limit_hidden_function_discovered_on/ +Reviewed-by: Werner Sembach +Reviewed-by: Ilpo Järvinen +Signed-off-by: Armin Wolf +Link: https://patch.msgid.link/20260512232145.329260-5-W_Armin@gmx.de +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + Documentation/admin-guide/laptops/uniwill-laptop.rst | 10 ++++++++++ + drivers/platform/x86/uniwill/uniwill-acpi.c | 4 ++-- + 2 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst +index 561334865feb7..1f3ca84c7d88b 100644 +--- a/Documentation/admin-guide/laptops/uniwill-laptop.rst ++++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst +@@ -43,6 +43,11 @@ Support for changing the platform performance mode is currently not implemented. + Battery Charging Control + ------------------------ + ++.. warning:: Some devices do not properly implement the charging threshold interface. Forcing ++ the driver to enable access to said interface on such devices might damage the ++ battery [1]_. Because of this the driver will not enable said feature even when ++ using the ``force`` module parameter. ++ + The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over + the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values + between 1 and 100 percent are supported. +@@ -70,3 +75,8 @@ The ``uniwill-laptop`` driver allows to set the configurable TGP for devices wit + allow it. + + See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. ++ ++References ++========== ++ ++.. [1] https://www.reddit.com/r/XMG_gg/comments/ld9yyf/battery_limit_hidden_function_discovered_on/ +diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c +index 540604c297715..bcd25d08f56b0 100644 +--- a/drivers/platform/x86/uniwill/uniwill-acpi.c ++++ b/drivers/platform/x86/uniwill/uniwill-acpi.c +@@ -2207,8 +2207,8 @@ static int __init uniwill_init(void) + } + + if (force) { +- /* Assume that the device supports all features */ +- device_descriptor.features = UINT_MAX; ++ /* Assume that the device supports all features except the charge limit */ ++ device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY; + pr_warn("Enabling potentially unsupported features\n"); + } + +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-uniwill-laptop-fix-behavior-of-force-mo.patch b/queue-7.0/platform-x86-uniwill-laptop-fix-behavior-of-force-mo.patch new file mode 100644 index 0000000000..6fe5b57aa7 --- /dev/null +++ b/queue-7.0/platform-x86-uniwill-laptop-fix-behavior-of-force-mo.patch @@ -0,0 +1,57 @@ +From 0d1b2f03b431a95559889b39f589c4ea5c9733fb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 01:21:40 +0200 +Subject: platform/x86: uniwill-laptop: Fix behavior of "force" module param +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Armin Wolf + +[ Upstream commit fb4b67c44557cb4cbb15900083d4e1af22320339 ] + +Users might want to force-enable all possible features even on +machines with a valid device descriptor. Until now the "force" +module param was ignored on such machines. Fix this to make +it easier to test for support of new features. + +Fixes: d050479693bb ("platform/x86: Add Uniwill laptop driver") +Reviewed-by: Werner Sembach +Reviewed-by: Ilpo Järvinen +Signed-off-by: Armin Wolf +Link: https://patch.msgid.link/20260512232145.329260-4-W_Armin@gmx.de +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/uniwill/uniwill-acpi.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c +index 07951e01b43db..540604c297715 100644 +--- a/drivers/platform/x86/uniwill/uniwill-acpi.c ++++ b/drivers/platform/x86/uniwill/uniwill-acpi.c +@@ -2189,8 +2189,6 @@ static int __init uniwill_init(void) + if (!force) + return -ENODEV; + +- /* Assume that the device supports all features */ +- device_descriptor.features = UINT_MAX; + pr_warn("Loading on a potentially unsupported device\n"); + } else { + /* +@@ -2208,6 +2206,12 @@ static int __init uniwill_init(void) + device_descriptor = *descriptor; + } + ++ if (force) { ++ /* Assume that the device supports all features */ ++ device_descriptor.features = UINT_MAX; ++ pr_warn("Enabling potentially unsupported features\n"); ++ } ++ + ret = platform_driver_register(&uniwill_driver); + if (ret < 0) + return ret; +-- +2.53.0 + diff --git a/queue-7.0/platform-x86-uniwill-laptop-properly-initialize-char.patch b/queue-7.0/platform-x86-uniwill-laptop-properly-initialize-char.patch new file mode 100644 index 0000000000..85742e23e5 --- /dev/null +++ b/queue-7.0/platform-x86-uniwill-laptop-properly-initialize-char.patch @@ -0,0 +1,97 @@ +From 5aec8935aaf57a90cbaac618f179a157efff9d57 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 01:21:38 +0200 +Subject: platform/x86: uniwill-laptop: Properly initialize charging threshold +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Armin Wolf + +[ Upstream commit c12cc42dadd85dea210d5699d4f21def827382eb ] + +The EC might initialize the charge threshold with 0 to signal that +said threshold is uninitialized. Detect this and replace said value +with 100 to signal the EC that we want to take control of battery +charging. Also set the threshold to 100 if the EC-provided value +is invalid. + +Fixes: d050479693bb ("platform/x86: Add Uniwill laptop driver") +Reviewed-by: Werner Sembach +Signed-off-by: Armin Wolf +Link: https://patch.msgid.link/20260512232145.329260-2-W_Armin@gmx.de +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/uniwill/uniwill-acpi.c | 35 ++++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c +index 6341dca20b76a..4b491fe8bdea4 100644 +--- a/drivers/platform/x86/uniwill/uniwill-acpi.c ++++ b/drivers/platform/x86/uniwill/uniwill-acpi.c +@@ -1193,6 +1193,16 @@ static int uniwill_led_init(struct uniwill_data *data) + &init_data); + } + ++static unsigned int uniwill_sanitize_battery_threshold(unsigned int value) ++{ ++ /* 0 means "charging threshold not active" */ ++ if (!value) ++ return 100; ++ ++ /* Guard against invalid values */ ++ return min(value, 100); ++} ++ + static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext, + void *drvdata, enum power_supply_property psp, + union power_supply_propval *val) +@@ -1239,7 +1249,8 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup + if (ret < 0) + return ret; + +- val->intval = clamp_val(FIELD_GET(CHARGE_CTRL_MASK, regval), 0, 100); ++ regval = FIELD_GET(CHARGE_CTRL_MASK, regval); ++ val->intval = uniwill_sanitize_battery_threshold(regval); + return 0; + default: + return -EINVAL; +@@ -1334,11 +1345,33 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt + + static int uniwill_battery_init(struct uniwill_data *data) + { ++ unsigned int value, threshold, sanitized; + int ret; + + if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) + return 0; + ++ ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * The charge control threshold might be initialized with 0 by ++ * the EC to signal that said threshold is uninitialized. We thus ++ * need to replace this placeholder value with a valid one (100) ++ * to signal that we want to take control of battery charging. ++ * For the sake of completeness we also apply this to other ++ * invalid threshold values. ++ */ ++ threshold = FIELD_GET(CHARGE_CTRL_MASK, value); ++ sanitized = uniwill_sanitize_battery_threshold(threshold); ++ if (threshold != sanitized) { ++ FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized); ++ ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); ++ if (ret < 0) ++ return ret; ++ } ++ + ret = devm_mutex_init(data->dev, &data->battery_lock); + if (ret < 0) + return ret; +-- +2.53.0 + diff --git a/queue-7.0/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch b/queue-7.0/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch new file mode 100644 index 0000000000..19fdc4ab92 --- /dev/null +++ b/queue-7.0/powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch @@ -0,0 +1,48 @@ +From 4e44da434abb3d129d900121aee350b5d7555dea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 16 Nov 2025 19:55:44 +0530 +Subject: powerpc: 82xx: fix uninitialized pointers with free attribute + +From: Ally Heev + +[ Upstream commit acd1e47db03d4b528fd5efb8565dd0de1c79f62a ] + +Uninitialized pointers with `__free` attribute can cause undefined +behavior as the memory allocated to the pointer is freed automatically +when the pointer goes out of scope. + +powerpc/km82xx doesn't have any bugs related to this as of now, but, +it is better to initialize and assign pointers with `__free` attribute +in one statement to ensure proper scope-based cleanup + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/ +Signed-off-by: Ally Heev +Fixes: 4aa5cc1e0012 ("powerpc-km82xx.c: replace of_node_put() with __free") +Reviewed-by: Christophe Leroy (CS GROUP) +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20251116-aheev-uninitialized-free-attr-km82xx-v2-1-4307e2b5300d@gmail.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/platforms/82xx/km82xx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c +index 99f0f0f418767..4ad223525e893 100644 +--- a/arch/powerpc/platforms/82xx/km82xx.c ++++ b/arch/powerpc/platforms/82xx/km82xx.c +@@ -27,8 +27,8 @@ + + static void __init km82xx_pic_init(void) + { +- struct device_node *np __free(device_node); +- np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); ++ struct device_node *np __free(device_node) = of_find_compatible_node(NULL, ++ NULL, "fsl,pq2-pic"); + + if (!np) { + pr_err("PIC init: can not find cpm-pic node\n"); +-- +2.53.0 + diff --git a/queue-7.0/powerpc-fix-dead-default-for-guest_state_buffer_test.patch b/queue-7.0/powerpc-fix-dead-default-for-guest_state_buffer_test.patch new file mode 100644 index 0000000000..e86682ced2 --- /dev/null +++ b/queue-7.0/powerpc-fix-dead-default-for-guest_state_buffer_test.patch @@ -0,0 +1,54 @@ +From 4629713be1234e854f2792f2f128ca777dcb914b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Apr 2026 17:15:45 +0100 +Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST + +From: Julian Braha + +[ Upstream commit aef656a0e6c01796190bb5bd2bdba1c644ed7811 ] + +The GUEST_STATE_BUFFER_TEST config option should default +to KUNIT_ALL_TESTS so that if all tests are enabled then +it is included, but currently the 'default KUNIT_ALL_TESTS' +statement is shadowed by 'def_tristate n', +meaning that this second default statement is currently dead code. + +It looks to me like the commit +6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +intended to set the default to KUNIT_ALL_TESTS, but mistakenly +missed the def_tristate. + +This dead code was found by kconfirm, a static analysis tool for Kconfig. + +Fixes: 6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") +Signed-off-by: Julian Braha +Tested-by: Gautam Menghani +Reviewed-by: Amit Machhiwal +Reviewed-by: Harsh Prateek Bora +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/Kconfig.debug | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug +index f15e5920080ba..e8718bc13eeb1 100644 +--- a/arch/powerpc/Kconfig.debug ++++ b/arch/powerpc/Kconfig.debug +@@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST + depends on DEBUG_KERNEL + + config GUEST_STATE_BUFFER_TEST +- def_tristate n ++ def_tristate KUNIT_ALL_TESTS + prompt "Enable Guest State Buffer unit tests" + depends on KUNIT + depends on KVM_BOOK3S_HV_POSSIBLE +- default KUNIT_ALL_TESTS + help + The Guest State Buffer is a data format specified in the PAPR. + It is by hcalls to communicate the state of L2 guests between +-- +2.53.0 + diff --git a/queue-7.0/powerpc-hv-gpci-fix-preempt-count-leak-in-sysfs-show.patch b/queue-7.0/powerpc-hv-gpci-fix-preempt-count-leak-in-sysfs-show.patch new file mode 100644 index 0000000000..709e517e53 --- /dev/null +++ b/queue-7.0/powerpc-hv-gpci-fix-preempt-count-leak-in-sysfs-show.patch @@ -0,0 +1,163 @@ +From a743f794116d95d0a911fdff24e719e183a41c83 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:42:56 +0530 +Subject: powerpc/hv-gpci: fix preempt count leak in sysfs show paths + +From: Aboorva Devarajan + +[ Upstream commit dbc30a57bd8e026995e9fa8e8c31cffd18542c01 ] + +Four sysfs show() callbacks in hv-gpci take get_cpu_var(hv_gpci_reqb) +(which calls preempt_disable()) but only call the matching put_cpu_var() +on the error path under the 'out:' label. Every successful read leaks +one preempt_disable(): + + processor_bus_topology_show() + processor_config_show() + affinity_domain_via_virtual_processor_show() + affinity_domain_via_domain_show() + +(affinity_domain_via_partition_show() was already correct.) + +On a CONFIG_PREEMPT=y kernel, repeated reads raise preempt_count and +eventually return to userspace with preemption still disabled. The +next user-mode page fault then hits faulthandler_disabled() == 1, +gets forced to SIGSEGV, and the resulting coredump trips +'BUG: scheduling while atomic' in call_usermodehelper_exec -> +wait_for_completion_state -> schedule: + + BUG: scheduling while atomic: //0x00000004 + ... + __schedule_bug+0x6c/0x90 + __schedule+0x58c/0x13a0 + schedule+0x48/0x1a0 + schedule_timeout+0x104/0x170 + wait_for_completion_state+0x16c/0x330 + call_usermodehelper_exec+0x254/0x2d0 + vfs_coredump+0x1050/0x2590 + get_signal+0xb9c/0xc80 + do_notify_resume+0xf8/0x470 + +Add an out_success label that calls put_cpu_var() before returning +the byte count, mirroring affinity_domain_via_partition_show(). + +Fixes: 71f1c39647d8 ("powerpc/hv_gpci: Add sysfs file inside hv_gpci device to show processor bus topology information") +Fixes: 1a160c2a13c6 ("powerpc/hv_gpci: Add sysfs file inside hv_gpci device to show processor config information") +Fixes: 71a7ccb478fc ("powerpc/hv_gpci: Add sysfs file inside hv_gpci device to show affinity domain via virtual processor information") +Fixes: a69a57cac1ec ("powerpc/hv_gpci: Add sysfs file inside hv_gpci device to show affinity domain via domain information") +Signed-off-by: Aboorva Devarajan +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260508041256.3447113-1-aboorvad@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/perf/hv-gpci.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c +index 5cac2cf3bd1e5..10c82cf8f5b39 100644 +--- a/arch/powerpc/perf/hv-gpci.c ++++ b/arch/powerpc/perf/hv-gpci.c +@@ -210,7 +210,7 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att + 0, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; +@@ -244,12 +244,14 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att + starting_index, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; + } + ++out_success: ++ put_cpu_var(hv_gpci_reqb); + return n; + + out: +@@ -278,7 +280,7 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute + 0, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; +@@ -312,12 +314,14 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute + starting_index, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; + } + ++out_success: ++ put_cpu_var(hv_gpci_reqb); + return n; + + out: +@@ -346,7 +350,7 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, + 0, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; +@@ -382,12 +386,14 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, + starting_index, secondary_index, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; + } + ++out_success: ++ put_cpu_var(hv_gpci_reqb); + return n; + + out: +@@ -416,7 +422,7 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device + 0, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; +@@ -448,12 +454,14 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device + starting_index, 0, buf, &n, arg); + + if (!ret) +- return n; ++ goto out_success; + + if (ret != H_PARAMETER) + goto out; + } + ++out_success: ++ put_cpu_var(hv_gpci_reqb); + return n; + + out: +-- +2.53.0 + diff --git a/queue-7.0/powerpc-time-remove-redundant-preempt_disable-enable.patch b/queue-7.0/powerpc-time-remove-redundant-preempt_disable-enable.patch new file mode 100644 index 0000000000..78b4e1e40a --- /dev/null +++ b/queue-7.0/powerpc-time-remove-redundant-preempt_disable-enable.patch @@ -0,0 +1,98 @@ +From 07f1046793bcc781cb92950337da5641814f61ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 13:44:13 +0530 +Subject: powerpc/time: Remove redundant preempt_disable|enable() calls from + arch_irq_work_raise() + +From: Sayali Patil + +[ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] + +A kernel panic is observed when handling machine check exceptions from +real mode. + + BUG: Unable to handle kernel data access on read at 0xc00000006be21300 + Oops: Kernel access of bad area, sig: 11 [#1] + MSR: 8000000000001003 CR: 88222248 XER: 00000005 + CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 + NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 + LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 + Call Trace: + [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 + [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 + +The crash occurs because arch_irq_work_raise() calls preempt_disable() +from machine check exception (MCE) handlers running in real mode. In +this context, accessing the preempt_count can fault, leading to the panic. + +The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix +oops due to perf_event_do_pending call") to avoid races while raising +irq work from exception context. + +Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when +queueing work on the local CPU") added preemption protection in +irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per +cpu atomics instead of regular atomics") added equivalent +protection in irq_work_queue_on() before reaching arch_irq_work_raise(): + + irq_work_queue() / irq_work_queue_on() + -> preempt_disable() + -> __irq_work_queue_local() + -> irq_work_raise() + -> arch_irq_work_raise() + +As a result, callers other than mce_irq_work_raise() already execute +with preemption disabled, making the additional +preempt_disable()/preempt_enable() pair in arch_irq_work_raise() +redundant. + +The arch_irq_work_raise() function executes in NMI context when called +from MCE handler. Hence we will not be preempted or scheduled out since +we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove +the preempt_disable()/preempt_enable() calls from here. + +Remove it to avoid accessing preempt_count from real mode context. + +Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") +Suggested-by: Mahesh Salgaonkar +Acked-by: Shrikanth Hegde +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Sayali Patil +[Maddy: Fixed the commit title] +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com +Signed-off-by: Sasha Levin +--- + arch/powerpc/kernel/time.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c +index 4bbeb8644d3da..b4472288e0d43 100644 +--- a/arch/powerpc/kernel/time.c ++++ b/arch/powerpc/kernel/time.c +@@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); + + #endif /* 32 vs 64 bit */ + ++/* ++ * Must be called with preemption disabled since it updates ++ * per-CPU irq_work state and programs the local CPU decrementer. ++ */ + void arch_irq_work_raise(void) + { + /* +@@ -471,10 +475,8 @@ void arch_irq_work_raise(void) + * which could get tangled up if we're messing with the same state + * here. + */ +- preempt_disable(); + set_irq_work_pending_flag(); + set_dec(1); +- preempt_enable(); + } + + static void set_dec_or_work(u64 val) +-- +2.53.0 + diff --git a/queue-7.0/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch b/queue-7.0/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch new file mode 100644 index 0000000000..33a68c26f4 --- /dev/null +++ b/queue-7.0/rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch @@ -0,0 +1,37 @@ +From a40c62177717d57f7153a2f757213e8c552fe3ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 12 May 2026 02:42:09 -0700 +Subject: RDMA/mana_ib: Report max_msg_sz in mana_ib_query_port + +From: Shiraz Saleem + +[ Upstream commit c9a40f6531b81baa9619bcc2697ff86896afcce7 ] + +Report max_msg_sz for mana_ib, which is 16MB. + +Fixes: 4bda1d5332ec ("RDMA/mana_ib: Implement port parameters") +Signed-off-by: Shiraz Saleem +Signed-off-by: Konstantin Taranov +Link: https://patch.msgid.link/20260512094209.264955-1-kotaranov@linux.microsoft.com +Reviewed-by: Long Li +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/hw/mana/main.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c +index 8d99cd00f002c..d913f885b3ef9 100644 +--- a/drivers/infiniband/hw/mana/main.c ++++ b/drivers/infiniband/hw/mana/main.c +@@ -639,6 +639,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port, + if (mana_ib_is_rnic(dev)) { + props->gid_tbl_len = 16; + props->ip_gids = true; ++ props->max_msg_sz = SZ_16M; + if (port == 1) + props->port_cap_flags = IB_PORT_CM_SUP; + } +-- +2.53.0 + diff --git a/queue-7.0/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch b/queue-7.0/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch new file mode 100644 index 0000000000..920bf13a50 --- /dev/null +++ b/queue-7.0/rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch @@ -0,0 +1,56 @@ +From b99bedacc5852cbd987d3731fbfefa9471aed4e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 19:38:34 +0800 +Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup + +From: Guangshuo Li + +[ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] + +In the error path of rtrs_srv_create_path_files(), the sysfs root folders +may already have been created and srv_path->kobj may already have been +initialized. If a later step fails, the cleanup currently calls +kobject_put(&srv_path->kobj) before +rtrs_srv_destroy_once_sysfs_root_folders(srv_path). + +kobject_put() may drop the last reference to srv_path->kobj and invoke the +release callback, rtrs_srv_release(), which frees srv_path. The following +call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then +dereferences srv_path internally to access srv_path->srv, resulting in a +use-after-free. + +This failure path is reached before rtrs_srv_create_path_files() returns +success, so the successful-path lifetime handling is not involved. + +Fix this by destroying the sysfs root folders before calling +kobject_put(&srv_path->kobj), so srv_path is still valid while the helper +accesses it. + +This issue was found by a static analysis tool I am developing. + +Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +index 51727c7d710c3..9dd9141c86a54 100644 +--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +@@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) + put_kobj: + kobject_del(&srv_path->kobj); + destroy_root: +- kobject_put(&srv_path->kobj); + rtrs_srv_destroy_once_sysfs_root_folders(srv_path); ++ kobject_put(&srv_path->kobj); + + return err; + } +-- +2.53.0 + diff --git a/queue-7.0/riscv-docs-fix-unmatched-quote-warning.patch b/queue-7.0/riscv-docs-fix-unmatched-quote-warning.patch new file mode 100644 index 0000000000..9205d4f85b --- /dev/null +++ b/queue-7.0/riscv-docs-fix-unmatched-quote-warning.patch @@ -0,0 +1,39 @@ +From 4551eade6573edb903c55a2927a10a6ed0a28a89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Apr 2026 16:23:04 -0700 +Subject: riscv: Docs: fix unmatched quote warning + +From: Randy Dunlap + +[ Upstream commit 50da1c9ccb70fc5250c37ac474b54ee072732ea3 ] + +'make htmldocs' complains about ``prctrl` -- so add a second '`' to +avoid the warning. + +Documentation/arch/riscv/zicfilp.rst:79: WARNING: Inline literal start-string without end-string. [docutils] + +Fixes: 08ee1559052b ("prctl: cfi: change the branch landing pad prctl()s to be more descriptive") +Signed-off-by: Randy Dunlap +Link: https://patch.msgid.link/20260406232304.1892528-1-rdunlap@infradead.org +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + Documentation/arch/riscv/zicfilp.rst | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Documentation/arch/riscv/zicfilp.rst b/Documentation/arch/riscv/zicfilp.rst +index ab7d8e62ddaff..12b35969d17ad 100644 +--- a/Documentation/arch/riscv/zicfilp.rst ++++ b/Documentation/arch/riscv/zicfilp.rst +@@ -78,7 +78,7 @@ the program. + + Per-task indirect branch tracking state can be monitored and + controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI` +-``prctl()` arguments (respectively), by supplying ++``prctl()`` arguments (respectively), by supplying + :c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument. These + are architecture-agnostic, and will return -EINVAL if the underlying + functionality is not supported. +-- +2.53.0 + diff --git a/queue-7.0/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch b/queue-7.0/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch new file mode 100644 index 0000000000..99b584ee04 --- /dev/null +++ b/queue-7.0/riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch @@ -0,0 +1,45 @@ +From 365c3ad50d79cc12c5b6392ce0645a2f48a2b3ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Apr 2026 09:11:39 +0000 +Subject: riscv: errata: Fix bitwise vs logical AND in MIPS errata patching + +From: Michael Neuling + +[ Upstream commit 4d2b03699460b8fd5df34408a03a84a1a7ff8aa1 ] + +The condition checking whether a specific errata needs patching uses +logical AND (&&) instead of bitwise AND (&). Since logical AND only +checks that both operands are non-zero, this causes all errata patches +to be applied whenever any single errata is detected, rather than only +applying the matching one. + +The SiFive errata implementation correctly uses bitwise AND for the same +check. + +Fixes: 0b0ca959d206 ("riscv: errata: Fix the PAUSE Opcode for MIPS P8700") +Signed-off-by: Michael Neuling +Assisted-by: Cursor:claude-4.6-opus-high-thinking +Link: https://patch.msgid.link/20260409091143.1348853-2-mikey@neuling.org +[pjw@kernel.org: fixed checkpatch warning] +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/errata/mips/errata.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/riscv/errata/mips/errata.c b/arch/riscv/errata/mips/errata.c +index e984a8152208c..2c3dc2259e93e 100644 +--- a/arch/riscv/errata/mips/errata.c ++++ b/arch/riscv/errata/mips/errata.c +@@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, + } + + tmp = (1U << alt->patch_id); +- if (cpu_req_errata && tmp) { ++ if (cpu_req_errata & tmp) { + mutex_lock(&text_mutex); + patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt), + alt->alt_len); +-- +2.53.0 + diff --git a/queue-7.0/riscv-fix-register-corruption-from-uninitialized-cre.patch b/queue-7.0/riscv-fix-register-corruption-from-uninitialized-cre.patch new file mode 100644 index 0000000000..cff638d4fa --- /dev/null +++ b/queue-7.0/riscv-fix-register-corruption-from-uninitialized-cre.patch @@ -0,0 +1,64 @@ +From f1aeb02785b2df05b6698af0584804dcae898618 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 1 May 2026 06:23:20 +0000 +Subject: riscv: Fix register corruption from uninitialized cregs on error + +From: Michael Neuling + +[ Upstream commit 6ebcbb53fc9bc30843054ed99fd60b8e542628f4 ] + +compat_riscv_gpr_set() calls cregs_to_regs() unconditionally, even when +user_regset_copyin() fails. Since cregs is an uninitialized stack +variable, a copyin failure causes uninitialized stack data to be written +into the target task's pt_regs, corrupting its register state and +potentially leaking kernel stack contents. + +compat_restore_sigcontext() has the same issue: it calls cregs_to_regs() +even when __copy_from_user() fails, leading to the same corruption of +the signal-returning task's register state on error. + +Only call cregs_to_regs() when the user copy succeeds. + +Fixes: 4608c159594f ("riscv: compat: ptrace: Add compat_arch_ptrace implement") +Fixes: 7383ee05314b ("riscv: compat: signal: Add rt_frame implementation") +Signed-off-by: Michael Neuling +Assisted-by: Cursor:claude-4.6-opus-high-thinking +Link: https://patch.msgid.link/20260501062320.2339562-1-mikey@neuling.org +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/kernel/compat_signal.c | 2 ++ + arch/riscv/kernel/ptrace.c | 4 ++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/kernel/compat_signal.c b/arch/riscv/kernel/compat_signal.c +index 6ec4e34255a9a..cf3eb33a11e46 100644 +--- a/arch/riscv/kernel/compat_signal.c ++++ b/arch/riscv/kernel/compat_signal.c +@@ -107,6 +107,8 @@ static long compat_restore_sigcontext(struct pt_regs *regs, + + /* sc_regs is structured the same as the start of pt_regs */ + err = __copy_from_user(&cregs, &sc->sc_regs, sizeof(sc->sc_regs)); ++ if (unlikely(err)) ++ return err; + + cregs_to_regs(&cregs, regs); + +diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c +index 93de2e7a30747..793bcee461828 100644 +--- a/arch/riscv/kernel/ptrace.c ++++ b/arch/riscv/kernel/ptrace.c +@@ -577,8 +577,8 @@ static int compat_riscv_gpr_set(struct task_struct *target, + struct compat_user_regs_struct cregs; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &cregs, 0, -1); +- +- cregs_to_regs(&cregs, task_pt_regs(target)); ++ if (!ret) ++ cregs_to_regs(&cregs, task_pt_regs(target)); + + return ret; + } +-- +2.53.0 + diff --git a/queue-7.0/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch b/queue-7.0/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch new file mode 100644 index 0000000000..dd8c785047 --- /dev/null +++ b/queue-7.0/riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch @@ -0,0 +1,87 @@ +From cb07da346f9347cd6aa65351546c092cd6980142 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 25 Jan 2026 00:52:12 -0500 +Subject: riscv: mm: Fixup no5lvl failure when vaddr is invalid +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guo Ren (Alibaba DAMO Academy) + +[ Upstream commit db909bd7986c10da074917af3dae83a60fa65093 ] + +Unlike no4lvl, no5lvl still continues to detect satp, which +requires va=pa mapping. When pa=0x800000000000, no5lvl +would fail in Sv48 mode due to an illegal VA value of +0x800000000000. + +So, prevent detecting the satp flow for no5lvl, when +vaddr is invalid. Add the is_vaddr_valid() function for +checking. + +Fixes: 26e7aacb83df ("riscv: Allow to downgrade paging mode from the command line") +Cc: Alexandre Ghiti +Cc: Björn Töpel +Signed-off-by: Guo Ren (Alibaba DAMO Academy) +Tested-by: Fangyu Yu +Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org +[pjw@kernel.org: cleaned up commit message] +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index 811e03786c560..1b221c3fe2750 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -846,6 +846,27 @@ static void __init set_mmap_rnd_bits_max(void) + mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3; + } + ++static bool __init is_vaddr_valid(unsigned long va) ++{ ++ unsigned long up = 0; ++ ++ switch (satp_mode) { ++ case SATP_MODE_39: ++ up = 1UL << 38; ++ break; ++ case SATP_MODE_48: ++ up = 1UL << 47; ++ break; ++ case SATP_MODE_57: ++ up = 1UL << 56; ++ break; ++ default: ++ return false; ++ } ++ ++ return (va < up) || (va >= (ULONG_MAX - up + 1)); ++} ++ + /* + * There is a simple way to determine if 4-level is supported by the + * underlying hardware: establish 1:1 mapping in 4-level page table mode +@@ -887,6 +908,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + set_satp_mode_pmd + PMD_SIZE, + PMD_SIZE, PAGE_KERNEL_EXEC); + retry: ++ if (!is_vaddr_valid(set_satp_mode_pmd)) ++ goto out; ++ + create_pgd_mapping(early_pg_dir, + set_satp_mode_pmd, + pgtable_l5_enabled ? +@@ -909,6 +933,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa) + disable_pgtable_l4(); + } + ++out: + memset(early_pg_dir, 0, PAGE_SIZE); + memset(early_p4d, 0, PAGE_SIZE); + memset(early_pud, 0, PAGE_SIZE); +-- +2.53.0 + diff --git a/queue-7.0/rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to-.patch b/queue-7.0/rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to-.patch new file mode 100644 index 0000000000..bb82eeada5 --- /dev/null +++ b/queue-7.0/rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to-.patch @@ -0,0 +1,658 @@ +From 6bb09ec0e619cdb31bf74772a9dcfc9fa42cbb99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 16 May 2026 00:05:14 +0100 +Subject: rxrpc: Fix DATA decrypt vs splice() by copying data to buffer in + recvmsg + +From: David Howells + +[ Upstream commit d2bc90cf6c75cb96d2ce549be6c35efa3099d25b ] + +This improves the fix for CVE-2026-43500. + +Fix the pagecache corruption from in-place decryption of a DATA packet +transmitted locally by splice() by getting rid of the packet sharing in the +I/O thread and unconditionally extracting the packet content into a bounce +buffer in which the buffer is decrypted. recvmsg() (or the kernel +equivalent) then copies the data from the bounce buffer to the destination +buffer. The sk_buff then remains unmodified. + +This has an additional advantage in that the packet is then arranged in the +buffer with the correct alignment required for the crypto algorithms to +process directly. The performance of the crypto does seem to be a little +faster and, surprisingly, the unencrypted performance doesn't seem to +change much - possibly due to removing complexity from the I/O thread. + +Yet another advantage is that the I/O thread doesn't have to copy packets +which would slow down packet distribution, ACK generation, etc.. + +The buffer belongs to the call and is allocated initially at 2K, +sufficiently large to hold a whole jumbo subpacket, but the buffer will be +increased in size if needed. However, to take this work, MSG_PEEK may +cause a later packet to be decrypted into the buffer, in which case the +earlier one will need re-decrypting for a subsequent recvmsg(). + +Note that rx_pkt_offset may legitimately see 0 as a valid offset now, so +switch to using USHRT_MAX to indicate an invalid offset. + +Note also that I would generally prefer to replace the buffers of the +current sk_buff with a new kmalloc'd buffer of the right size, ditching the +old data and frags as this makes the handling of MSG_PEEK easier and +removes the re-decryption issue, but this looks like quite a complicated +thing to achieve. skb_morph() looks half way to what I want, but I don't +want to have to allocate a new sk_buff. + +Fixes: d0d5c0cd1e71 ("rxrpc: Use skb_unshare() rather than skb_cow_data()") +Reported-by: Hyunwoo Kim +Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/ +Signed-off-by: David Howells +cc: Simon Horman +cc: Jiayuan Chen +cc: linux-afs@lists.infradead.org +Reviewed-by: Jeffrey Altman +Tested-by: Marc Dionne +Link: https://patch.msgid.link/20260515230516.2718212-3-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rxrpc/ar-internal.h | 7 +++- + net/rxrpc/call_event.c | 22 +---------- + net/rxrpc/call_object.c | 2 + + net/rxrpc/insecure.c | 3 -- + net/rxrpc/recvmsg.c | 68 +++++++++++++++++++++++++------- + net/rxrpc/rxgk.c | 51 ++++++++++++------------ + net/rxrpc/rxgk_common.h | 82 +++++++++++++++++++++++++++++++++++++++ + net/rxrpc/rxkad.c | 86 +++++++++++++++-------------------------- + 8 files changed, 201 insertions(+), 120 deletions(-) + +diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h +index 27c2aa2dd023c..783367eea798b 100644 +--- a/net/rxrpc/ar-internal.h ++++ b/net/rxrpc/ar-internal.h +@@ -213,8 +213,6 @@ struct rxrpc_skb_priv { + struct { + u16 offset; /* Offset of data */ + u16 len; /* Length of data */ +- u8 flags; +-#define RXRPC_RX_VERIFIED 0x01 + }; + struct { + rxrpc_seq_t first_ack; /* First packet in acks table */ +@@ -774,6 +772,11 @@ struct rxrpc_call { + struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ + struct sk_buff_head rx_queue; /* Queue of packets for this call to receive */ + struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */ ++ void *rx_dec_buffer; /* Decryption buffer */ ++ unsigned short rx_dec_bsize; /* rx_dec_buffer size */ ++ unsigned short rx_dec_offset; /* Decrypted packet data offset */ ++ unsigned short rx_dec_len; /* Decrypted packet data len */ ++ rxrpc_seq_t rx_dec_seq; /* Packet in decryption buffer */ + + rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */ + rxrpc_seq_t rx_consumed; /* Highest packet consumed */ +diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c +index 2b19b252225e5..fec59d9338b9f 100644 +--- a/net/rxrpc/call_event.c ++++ b/net/rxrpc/call_event.c +@@ -332,27 +332,7 @@ bool rxrpc_input_call_event(struct rxrpc_call *call) + + saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK; + +- if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && +- sp->hdr.securityIndex != 0 && +- (skb_cloned(skb) || +- skb_has_frag_list(skb) || +- skb_has_shared_frag(skb))) { +- /* Unshare the packet so that it can be +- * modified by in-place decryption. +- */ +- struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); +- +- if (nskb) { +- rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); +- rxrpc_input_call_packet(call, nskb); +- rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx); +- } else { +- /* OOM - Drop the packet. */ +- rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); +- } +- } else { +- rxrpc_input_call_packet(call, skb); +- } ++ rxrpc_input_call_packet(call, skb); + rxrpc_free_skb(skb, rxrpc_skb_put_call_rx); + did_receive = true; + } +diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c +index f035f486c1397..fcb9d38bb5214 100644 +--- a/net/rxrpc/call_object.c ++++ b/net/rxrpc/call_object.c +@@ -152,6 +152,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, + spin_lock_init(&call->notify_lock); + refcount_set(&call->ref, 1); + call->debug_id = debug_id; ++ call->rx_pkt_offset = USHRT_MAX; + call->tx_total_len = -1; + call->tx_jumbo_max = 1; + call->next_rx_timo = 20 * HZ; +@@ -553,6 +554,7 @@ static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call) + rxrpc_purge_queue(&call->recvmsg_queue); + rxrpc_purge_queue(&call->rx_queue); + rxrpc_purge_queue(&call->rx_oos_queue); ++ kfree(call->rx_dec_buffer); + } + + /* +diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c +index 0a260df45d25a..7a26c6097d033 100644 +--- a/net/rxrpc/insecure.c ++++ b/net/rxrpc/insecure.c +@@ -32,9 +32,6 @@ static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) + + static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) + { +- struct rxrpc_skb_priv *sp = rxrpc_skb(skb); +- +- sp->flags |= RXRPC_RX_VERIFIED; + return 0; + } + +diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c +index e1f7513a46dbe..c940600117a48 100644 +--- a/net/rxrpc/recvmsg.c ++++ b/net/rxrpc/recvmsg.c +@@ -147,15 +147,52 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) + } + + /* +- * Decrypt and verify a DATA packet. ++ * Decrypt and verify a DATA packet. The content of the packet is pulled out ++ * into a flat buffer rather than decrypting in place in the skbuff. This also ++ * has the advantage of aligning the buffer correctly for the crypto routines. ++ * ++ * We keep track of the sequence number of the packet currently decrypted into ++ * the buffer in ->rx_dec_seq. If MSG_PEEK is used and steps onto a new ++ * packet, subsequent recvmsg() calls will have to go back and re-decrypt the ++ * current packet. + */ + static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) + { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); ++ int ret; + +- if (sp->flags & RXRPC_RX_VERIFIED) +- return 0; +- return call->security->verify_packet(call, skb); ++ if (sp->len > call->rx_dec_bsize) { ++ /* Make sure we can hold a 1412-byte jumbo subpacket and make ++ * sure that the buffer size is aligned to a crypto blocksize. ++ */ ++ size_t size = clamp(round_up(sp->len, 32), 2048, 65535); ++ void *buffer = krealloc(call->rx_dec_buffer, size, GFP_NOFS); ++ ++ if (!buffer) ++ return -ENOMEM; ++ call->rx_dec_buffer = buffer; ++ call->rx_dec_bsize = size; ++ } ++ ++ ret = -EFAULT; ++ if (skb_copy_bits(skb, sp->offset, call->rx_dec_buffer, sp->len) < 0) ++ goto err; ++ ++ call->rx_dec_offset = 0; ++ call->rx_dec_len = sp->len; ++ call->rx_dec_seq = sp->hdr.seq; ++ ret = call->security->verify_packet(call, skb); ++ if (ret < 0) ++ goto err; ++ return 0; ++ ++err: ++ kfree(call->rx_dec_buffer); ++ call->rx_dec_buffer = NULL; ++ call->rx_dec_bsize = 0; ++ call->rx_dec_offset = 0; ++ call->rx_dec_len = 0; ++ return ret; + } + + /* +@@ -283,16 +320,21 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + if (msg) + sock_recv_timestamp(msg, sock->sk, skb); + +- if (rx_pkt_offset == 0) { ++ if (call->rx_dec_seq != sp->hdr.seq || ++ !call->rx_dec_buffer) { + ret2 = rxrpc_verify_data(call, skb); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq, +- sp->offset, sp->len, ret2); ++ call->rx_dec_offset, ++ call->rx_dec_len, ret2); + if (ret2 < 0) { + ret = ret2; + goto out; + } +- rx_pkt_offset = sp->offset; +- rx_pkt_len = sp->len; ++ } ++ ++ if (rx_pkt_offset == USHRT_MAX) { ++ rx_pkt_offset = call->rx_dec_offset; ++ rx_pkt_len = call->rx_dec_len; + } else { + trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, + rx_pkt_offset, rx_pkt_len, 0); +@@ -304,10 +346,10 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + if (copy > remain) + copy = remain; + if (copy > 0) { +- ret2 = skb_copy_datagram_iter(skb, rx_pkt_offset, iter, +- copy); +- if (ret2 < 0) { +- ret = ret2; ++ ret2 = copy_to_iter(call->rx_dec_buffer + rx_pkt_offset, ++ copy, iter); ++ if (ret2 != copy) { ++ ret = -EFAULT; + goto out; + } + +@@ -328,7 +370,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + /* The whole packet has been transferred. */ + if (sp->hdr.flags & RXRPC_LAST_PACKET) + ret = 1; +- rx_pkt_offset = 0; ++ rx_pkt_offset = USHRT_MAX; + rx_pkt_len = 0; + + skb = skb_peek_next(skb, &call->recvmsg_queue); +diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c +index 26e723052a37e..f81703ee7ac32 100644 +--- a/net/rxrpc/rxgk.c ++++ b/net/rxrpc/rxgk.c +@@ -473,8 +473,9 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_header *hdr; + struct krb5_buffer metadata; +- unsigned int offset = sp->offset, len = sp->len; ++ unsigned int len = call->rx_dec_len; + size_t data_offset = 0, data_len = len; ++ void *data = call->rx_dec_buffer, *p = data; + u32 ac = 0; + int ret = -ENOMEM; + +@@ -500,16 +501,15 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + + metadata.len = sizeof(*hdr); + metadata.data = hdr; +- ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata, +- skb, &offset, &len, &ac); ++ ret = rxgk_verify_mic(gk->krb5, gk->rx_Kc, &metadata, &p, &len, &ac); + kfree(hdr); + if (ret < 0) { + if (ret != -ENOMEM) + rxrpc_abort_eproto(call, skb, ac, + rxgk_abort_1_verify_mic_eproto); + } else { +- sp->offset = offset; +- sp->len = len; ++ call->rx_dec_offset = p - data; ++ call->rx_dec_len = len; + } + + put_gk: +@@ -526,56 +526,53 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + struct sk_buff *skb) + { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); +- struct rxgk_header hdr; +- unsigned int offset = sp->offset, len = sp->len; ++ struct rxgk_header *hdr; ++ unsigned int offset = 0, len = call->rx_dec_len; ++ void *data = call->rx_dec_buffer, *p = data; + int ret; + u32 ac = 0; + + _enter(""); + + if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE, +- len, sizeof(hdr)) < 0) { ++ len, sizeof(*hdr)) < 0) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_header); + goto error; + } + +- ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); ++ ret = rxgk_decrypt(gk->krb5, gk->rx_enc, &p, &len, &ac); + if (ret < 0) { + if (ret != -ENOMEM) + rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto); + goto error; + } ++ offset = p - data; + +- if (len < sizeof(hdr)) { ++ if (len < sizeof(*hdr)) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_header); + goto error; + } + + /* Extract the header from the skb */ +- ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr)); +- if (ret < 0) { +- ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, +- rxgk_abort_2_short_encdata); +- goto error; +- } +- offset += sizeof(hdr); +- len -= sizeof(hdr); +- +- if (ntohl(hdr.epoch) != call->conn->proto.epoch || +- ntohl(hdr.cid) != call->cid || +- ntohl(hdr.call_number) != call->call_id || +- ntohl(hdr.seq) != sp->hdr.seq || +- ntohl(hdr.sec_index) != call->security_ix || +- ntohl(hdr.data_len) > len) { ++ hdr = data + offset; ++ offset += sizeof(*hdr); ++ len -= sizeof(*hdr); ++ ++ if (ntohl(hdr->epoch) != call->conn->proto.epoch || ++ ntohl(hdr->cid) != call->cid || ++ ntohl(hdr->call_number) != call->call_id || ++ ntohl(hdr->seq) != sp->hdr.seq || ++ ntohl(hdr->sec_index) != call->security_ix || ++ ntohl(hdr->data_len) > len) { + ret = rxrpc_abort_eproto(call, skb, RXGK_SEALEDINCON, + rxgk_abort_2_short_data); + goto error; + } + +- sp->offset = offset; +- sp->len = ntohl(hdr.data_len); ++ call->rx_dec_offset = offset; ++ call->rx_dec_len = ntohl(hdr->data_len); + ret = 0; + error: + rxgk_put(gk); +diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h +index 1e257d7ab8ec1..112b5366ce119 100644 +--- a/net/rxrpc/rxgk_common.h ++++ b/net/rxrpc/rxgk_common.h +@@ -105,6 +105,49 @@ int rxgk_decrypt_skb(const struct krb5_enctype *krb5, + return ret; + } + ++/* ++ * Apply decryption and checksumming functions a flat data buffer. The data ++ * point and length are updated to reflect the actual content of the encrypted ++ * region. ++ */ ++static inline int rxgk_decrypt(const struct krb5_enctype *krb5, ++ struct crypto_aead *aead, ++ void **_data, unsigned int *_len, ++ int *_error_code) ++{ ++ struct scatterlist sg[1]; ++ size_t offset = 0, len = *_len; ++ int ret; ++ ++ sg_init_one(sg, *_data, len); ++ ++ ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len); ++ switch (ret) { ++ case 0: ++ if (offset & 3) { ++ *_error_code = RXGK_INCONSISTENCY; ++ ret = -EPROTO; ++ break; ++ } ++ *_data += offset; ++ *_len = len; ++ break; ++ case -EBADMSG: /* Checksum mismatch. */ ++ case -EPROTO: ++ *_error_code = RXGK_SEALEDINCON; ++ break; ++ case -EMSGSIZE: ++ *_error_code = RXGK_PACKETSHORT; ++ break; ++ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ ++ default: ++ *_error_code = RXGK_INCONSISTENCY; ++ break; ++ } ++ ++ return ret; ++} ++ + /* + * Check the MIC on a region of an skbuff. The offset and length are updated + * to reflect the actual content of the secure region. +@@ -148,3 +191,42 @@ int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, + + return ret; + } ++ ++/* ++ * Check the MIC on a flat buffer. The data pointer and length are updated to ++ * reflect the actual content of the secure region. ++ */ ++static inline ++int rxgk_verify_mic(const struct krb5_enctype *krb5, ++ struct crypto_shash *shash, ++ const struct krb5_buffer *metadata, ++ void **_data, unsigned int *_len, ++ u32 *_error_code) ++{ ++ struct scatterlist sg[1]; ++ size_t offset = 0, len = *_len; ++ int ret; ++ ++ sg_init_one(sg, *_data, len); ++ ++ ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len); ++ switch (ret) { ++ case 0: ++ *_data += offset; ++ *_len = len; ++ break; ++ case -EBADMSG: /* Checksum mismatch */ ++ case -EPROTO: ++ *_error_code = RXGK_SEALEDINCON; ++ break; ++ case -EMSGSIZE: ++ *_error_code = RXGK_PACKETSHORT; ++ break; ++ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ ++ default: ++ *_error_code = RXGK_INCONSISTENCY; ++ break; ++ } ++ ++ return ret; ++} +diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c +index cba7935977f0b..0759363378363 100644 +--- a/net/rxrpc/rxkad.c ++++ b/net/rxrpc/rxkad.c +@@ -430,27 +430,25 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + rxrpc_seq_t seq, + struct skcipher_request *req) + { +- struct rxkad_level1_hdr sechdr; ++ struct rxkad_level1_hdr *sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_crypt iv; +- struct scatterlist sg[16]; +- u32 data_size, buf; ++ struct scatterlist sg[1]; ++ void *data = call->rx_dec_buffer; ++ u32 len = sp->len, data_size, buf; + u16 check; + int ret; + + _enter(""); + +- if (sp->len < 8) ++ if (len < 8) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_1_short_header); + + /* Decrypt the skbuff in-place. TODO: We really want to decrypt + * directly into the target buffer. + */ +- sg_init_table(sg, ARRAY_SIZE(sg)); +- ret = skb_to_sgvec(skb, sg, sp->offset, 8); +- if (unlikely(ret < 0)) +- return ret; ++ sg_init_one(sg, data, len); + + /* start the decryption afresh */ + memset(&iv, 0, sizeof(iv)); +@@ -464,13 +462,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + return ret; + + /* Extract the decrypted packet length */ +- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) +- return rxrpc_abort_eproto(call, skb, RXKADDATALEN, +- rxkad_abort_1_short_encdata); +- sp->offset += sizeof(sechdr); +- sp->len -= sizeof(sechdr); ++ sechdr = data; ++ call->rx_dec_offset = sizeof(*sechdr); ++ len -= sizeof(*sechdr); + +- buf = ntohl(sechdr.data_size); ++ buf = ntohl(sechdr->data_size); + data_size = buf & 0xffff; + + check = buf >> 16; +@@ -479,10 +475,10 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + if (check != 0) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_1_short_check); +- if (data_size > sp->len) ++ if (data_size > len) + return rxrpc_abort_eproto(call, skb, RXKADDATALEN, + rxkad_abort_1_short_data); +- sp->len = data_size; ++ call->rx_dec_len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; +@@ -496,43 +492,28 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + struct skcipher_request *req) + { + const struct rxrpc_key_token *token; +- struct rxkad_level2_hdr sechdr; ++ struct rxkad_level2_hdr *sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_crypt iv; +- struct scatterlist _sg[4], *sg; +- u32 data_size, buf; ++ struct scatterlist sg[1]; ++ void *data = call->rx_dec_buffer; ++ u32 len = sp->len, data_size, buf; + u16 check; +- int nsg, ret; ++ int ret; + +- _enter(",{%d}", sp->len); ++ _enter(",{%d}", len); + +- if (sp->len < 8) ++ if (len < 8) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_2_short_header); + + /* Don't let the crypto algo see a misaligned length. */ +- sp->len = round_down(sp->len, 8); ++ len = round_down(len, 8); + +- /* Decrypt the skbuff in-place. TODO: We really want to decrypt +- * directly into the target buffer. ++ /* Decrypt in place in the call's decryption buffer. TODO: We really ++ * want to decrypt directly into the target buffer. + */ +- sg = _sg; +- nsg = skb_shinfo(skb)->nr_frags + 1; +- if (nsg <= 4) { +- nsg = 4; +- } else { +- sg = kmalloc_objs(*sg, nsg, GFP_NOIO); +- if (!sg) +- return -ENOMEM; +- } +- +- sg_init_table(sg, nsg); +- ret = skb_to_sgvec(skb, sg, sp->offset, sp->len); +- if (unlikely(ret < 0)) { +- if (sg != _sg) +- kfree(sg); +- return ret; +- } ++ sg_init_one(sg, data, len); + + /* decrypt from the session key */ + token = call->conn->key->payload.data[0]; +@@ -540,11 +521,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + + skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); + skcipher_request_set_callback(req, 0, NULL, NULL); +- skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x); ++ skcipher_request_set_crypt(req, sg, sg, len, iv.x); + ret = crypto_skcipher_decrypt(req); + skcipher_request_zero(req); +- if (sg != _sg) +- kfree(sg); + if (ret < 0) { + if (ret == -ENOMEM) + return ret; +@@ -553,13 +532,11 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + } + + /* Extract the decrypted packet length */ +- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) +- return rxrpc_abort_eproto(call, skb, RXKADDATALEN, +- rxkad_abort_2_short_len); +- sp->offset += sizeof(sechdr); +- sp->len -= sizeof(sechdr); ++ sechdr = data; ++ call->rx_dec_offset = sizeof(*sechdr); ++ len -= sizeof(*sechdr); + +- buf = ntohl(sechdr.data_size); ++ buf = ntohl(sechdr->data_size); + data_size = buf & 0xffff; + + check = buf >> 16; +@@ -569,17 +546,18 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_2_short_check); + +- if (data_size > sp->len) ++ if (data_size > len) + return rxrpc_abort_eproto(call, skb, RXKADDATALEN, + rxkad_abort_2_short_data); + +- sp->len = data_size; ++ call->rx_dec_len = data_size; + _leave(" = 0 [dlen=%x]", data_size); + return 0; + } + + /* +- * Verify the security on a received packet and the subpackets therein. ++ * Verify the security on a received (sub)packet. If the packet needs ++ * modifying (e.g. decrypting), it must be copied. + */ + static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) + { +-- +2.53.0 + diff --git a/queue-7.0/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch b/queue-7.0/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch new file mode 100644 index 0000000000..2da40a02d1 --- /dev/null +++ b/queue-7.0/scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch @@ -0,0 +1,41 @@ +From 2721f14115b9aaa8e65f69fe29b7813cf71b2f6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 12:53:17 -0500 +Subject: scsi: sd: Fix return code handling in sd_spinup_disk() + +From: Mike Christie + +[ Upstream commit 6ea68a8dc7d2711504d944811981a5304af7d7a9 ] + +As found by smatch-ci, scsi_execute_cmd() can return negative or positve +values so we should use a int instead of unsigned int. + +Fixes: b4d0c33a32c3 ("scsi: sd: Fix sshdr use in sd_spinup_disk") +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u +Signed-off-by: Mike Christie +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/sd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c +index aba22060fcd50..79136d943595c 100644 +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -2430,8 +2430,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) + { + static const u8 cmd[10] = { TEST_UNIT_READY }; + unsigned long spintime_expire = 0; +- int spintime, sense_valid = 0; +- unsigned int the_result; ++ int the_result, spintime, sense_valid = 0; + struct scsi_sense_hdr sshdr; + struct scsi_failure failure_defs[] = { + /* Do not retry Medium Not Present */ +-- +2.53.0 + diff --git a/queue-7.0/selftests-net-fix-checksums-in-xdp_native.patch b/queue-7.0/selftests-net-fix-checksums-in-xdp_native.patch new file mode 100644 index 0000000000..5826039f19 --- /dev/null +++ b/queue-7.0/selftests-net-fix-checksums-in-xdp_native.patch @@ -0,0 +1,161 @@ +From 8e21afb72cd6487ceeb5da2f7384b690db800ad7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 18:39:28 +0300 +Subject: selftests: net: Fix checksums in xdp_native + +From: Nimrod Oren + +[ Upstream commit dfc077043351a81887d1e4c9ac244e9243f3cbf2 ] + +Data adjustment cases failed with "Data exchange failed" when using IPv4 +because the program did not update the IP and UDP checksums in the IPv4 +branch. The issue was masked when both IPv4 and IPv6 were configured, +since the test harness prefers IPv6. + +While here, generalize csum_fold_helper() to fold twice so it works for +any 32-bit input. + +Fixes: 0b65cfcef9c5 ("selftests: drv-net: Test tail-adjustment support") +Reviewed-by: Carolina Jubran +Reviewed-by: Dragos Tatulea +Signed-off-by: Nimrod Oren +Link: https://patch.msgid.link/20260520153928.3371765-1-noren@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../selftests/net/lib/xdp_native.bpf.c | 55 ++++++++++--------- + 1 file changed, 30 insertions(+), 25 deletions(-) + +diff --git a/tools/testing/selftests/net/lib/xdp_native.bpf.c b/tools/testing/selftests/net/lib/xdp_native.bpf.c +index 64f05229ab243..ded3f896e6224 100644 +--- a/tools/testing/selftests/net/lib/xdp_native.bpf.c ++++ b/tools/testing/selftests/net/lib/xdp_native.bpf.c +@@ -268,6 +268,17 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port) + return XDP_PASS; + } + ++static __always_inline __u16 csum_fold_helper(__u32 csum) ++{ ++ csum = (csum & 0xffff) + (csum >> 16); ++ return ~((csum & 0xffff) + (csum >> 16)); ++} ++ ++static __always_inline __u16 csum_fold_udp_helper(__u32 csum) ++{ ++ return csum_fold_helper(csum) ? : 0xffff; ++} ++ + static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + { + void *data_end = (void *)(long)ctx->data_end; +@@ -281,21 +292,22 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + + if (eth->h_proto == bpf_htons(ETH_P_IP)) { + struct iphdr *iph = data + sizeof(*eth); +- __u16 total_len; + + if (iph + 1 > (struct iphdr *)data_end) + return NULL; + +- iph->tot_len = bpf_htons(bpf_ntohs(iph->tot_len) + offset); +- + udph = (void *)eth + sizeof(*iph) + sizeof(*eth); + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + +- len_new = bpf_htons(bpf_ntohs(udph->len) + offset); ++ len = iph->tot_len; ++ len_new = bpf_htons(bpf_ntohs(len) + offset); ++ iph->tot_len = len_new; ++ iph->check = csum_fold_helper( ++ bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), ~((__u32)iph->check))); + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { + struct ipv6hdr *ipv6h = data + sizeof(*eth); +- __u16 payload_len; + + if (ipv6h + 1 > (struct ipv6hdr *)data_end) + return NULL; +@@ -304,33 +316,27 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) + if (!udph || udph + 1 > (struct udphdr *)data_end) + return NULL; + +- *udp_csum = ~((__u32)udph->check); +- + len = ipv6h->payload_len; + len_new = bpf_htons(bpf_ntohs(len) + offset); + ipv6h->payload_len = len_new; +- +- *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, +- sizeof(len_new), *udp_csum); +- +- len = udph->len; +- len_new = bpf_htons(bpf_ntohs(udph->len) + offset); +- *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, +- sizeof(len_new), *udp_csum); + } else { + return NULL; + } + ++ len = udph->len; ++ len_new = bpf_htons(bpf_ntohs(len) + offset); ++ ++ *udp_csum = ~((__u32)udph->check); ++ *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), *udp_csum); ++ *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, ++ sizeof(len_new), *udp_csum); ++ + udph->len = len_new; + + return udph; + } + +-static __u16 csum_fold_helper(__u32 csum) +-{ +- return ~((csum & 0xffff) + (csum >> 16)) ? : 0xffff; +-} +- + static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, + unsigned long hdr_len) + { +@@ -359,7 +365,7 @@ static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (bpf_xdp_adjust_tail(ctx, 0 - offset) < 0) + return -1; +@@ -403,7 +409,7 @@ static int xdp_adjst_tail_grow_data(struct xdp_md *ctx, __u16 offset) + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)tmp_buff, offset, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + buff_len = bpf_xdp_get_buff_len(ctx); + +@@ -484,8 +490,7 @@ static int xdp_adjst_head_shrnk_data(struct xdp_md *ctx, __u64 hdr_len, + return -1; + + udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); +- +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (bpf_xdp_load_bytes(ctx, 0, tmp_buff, MAX_ADJST_OFFSET) < 0) + return -1; +@@ -542,7 +547,7 @@ static int xdp_adjst_head_grow_data(struct xdp_md *ctx, __u64 hdr_len, + return -1; + + udp_csum = bpf_csum_diff(0, 0, (__be32 *)data_buff, offset, udp_csum); +- udph->check = (__u16)csum_fold_helper(udp_csum); ++ udph->check = (__u16)csum_fold_udp_helper(udp_csum); + + if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0) + return -1; +-- +2.53.0 + diff --git a/queue-7.0/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch b/queue-7.0/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch new file mode 100644 index 0000000000..25d184de6e --- /dev/null +++ b/queue-7.0/selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch @@ -0,0 +1,61 @@ +From cdabb265efcef19de5f4f34e989dd630fca225ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 18:19:40 +0800 +Subject: selftests: ublk: cap nthreads to kernel's actual nr_hw_queues + +From: Ming Lei + +[ Upstream commit 87d0740b7c4cc847be1b6f307ab6d8547cb1a726 ] + +dev->nthreads is derived from the user-requested queue count before the +ADD command, but the kernel may reduce nr_hw_queues (capped to +nr_cpu_ids). When the VM has fewer CPUs than requested queues, the +daemon creates more handler threads than there are kernel queues. + +In non-batch mode, the extra threads access uninitialized queues +(q_depth=0), submit zero io_uring SQEs, and block forever in +io_cqring_wait. In batch mode, the extra threads cause similar hangs +during device removal. + +In both cases, the stuck threads prevent the daemon from closing the +char device, holding the last ublk_device reference and causing +ublk_ctrl_del_dev() to hang in wait_event_interruptible(). + +Fix by capping dev->nthreads to the kernel-returned nr_hw_queues after +the ADD command completes. per_io_tasks mode is excluded because threads +interleave across all queues, so nthreads > nr_hw_queues is valid. + +Fixes: abe54c160346 ("selftests: ublk: kublk: decouple ublk_queues from ublk server threads") +Signed-off-by: Ming Lei +Link: https://patch.msgid.link/20260513101941.1373998-1-tom.leiming@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/ublk/kublk.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c +index e1c3b3c55e565..c40aa7952b6eb 100644 +--- a/tools/testing/selftests/ublk/kublk.c ++++ b/tools/testing/selftests/ublk/kublk.c +@@ -1395,6 +1395,17 @@ static int __cmd_dev_add(const struct dev_ctx *ctx) + goto fail; + } + ++ /* ++ * The kernel may reduce nr_hw_queues (e.g. capped to nr_cpu_ids). ++ * Cap nthreads to the actual queue count to avoid creating extra ++ * handler threads that will hang during device removal. ++ * ++ * per_io_tasks mode is excluded: threads interleave across all ++ * queues so nthreads > nr_hw_queues is valid and intentional. ++ */ ++ if (!ctx->per_io_tasks && dev->nthreads > info->nr_hw_queues) ++ dev->nthreads = info->nr_hw_queues; ++ + ret = ublk_start_daemon(ctx, dev); + ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\n", __func__, ret); + if (ret < 0) +-- +2.53.0 + diff --git a/queue-7.0/series b/queue-7.0/series index e61d6b97c2..c53462ee91 100644 --- a/queue-7.0/series +++ b/queue-7.0/series @@ -171,3 +171,291 @@ hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.pat hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch +pinctrl-mediatek-moore-implement-gpio_chip-get_direc.patch +pinctrl-qcom-ipq4019-mark-gpio-as-a-gpio-pin-functio.patch +arm64-dts-renesas-r8a78000-fix-scif-brg_int-clocks.patch +arm-dts-renesas-genmai-drop-superfluous-cells.patch +arm-dts-renesas-rskrza1-drop-superfluous-cells.patch +pinctrl-renesas-rzg2l-fix-incorrect-pupd-register-of.patch +pinctrl-renesas-rzg2l-fix-smt-register-cache-handlin.patch +pinctrl-meson-amlogic-a4-fix-deadlock-issue.patch +pinctrl-qcom-fix-gpio-to-pdc-wake-irq-map-for-qcs615.patch +kho-skip-kho-for-crash-kernel.patch +mm-memfd_luo-report-error-when-restoring-a-folio-fai.patch +hid-intel-thc-hid-intel-quickspi-fix-some-error-code.patch +hid-uclogic-fix-regression-of-input-name-assignment.patch +firmware-arm_ffa-check-for-null-ff-a-id-table-while-.patch +firmware-arm_ffa-skip-free_pages-on-rx-buffer-alloc-.patch +firmware-arm_ffa-fix-per-vcpu-self-notifications-han.patch +firmware-arm_ffa-unregister-bus-notifier-on-teardown.patch +riscv-errata-fix-bitwise-vs-logical-and-in-mips-erra.patch +riscv-fix-register-corruption-from-uninitialized-cre.patch +riscv-mm-fixup-no5lvl-failure-when-vaddr-is-invalid.patch +kunit-config-enable-kunit_debugfs-by-default.patch +kunit-config-kunit_debugfs-should-depend-on-debug_fs.patch +pinctrl-qcom-fix-wakeirq-map-by-removing-disconnecte.patch +firmware-arm_ffa-bound-partition_info_get_regs-copie.patch +firmware-arm_ffa-keep-framework-rx-release-under-loc.patch +firmware-arm_ffa-validate-framework-notification-mes.patch +firmware-arm_ffa-align-rxtx-buffer-size-before-mappi.patch +firmware-arm_ffa-snapshot-notifier-callbacks-under-l.patch +firmware-arm_ffa-fix-sched-recv-callback-partition-l.patch +arm-integrator-fix-early-initialization.patch +alsa-hda-cs35l56-put-acpi-device-after-setting-compa.patch +alsa-hda-cs35l41-put-acpi-device-on-missing-physical.patch +btrfs-tracepoints-fix-sleep-while-in-atomic-context-.patch +netfilter-x_tables-allow-initial-table-replace-witho.patch +netfilter-x_tables-allocate-hook-ops-while-under-mut.patch +netfilter-x_tables-unregister-the-templates-first.patch +netfilter-x_tables-add-and-use-xt_unregister_table_p.patch +netfilter-x_tables-add-and-use-xtables_unregister_ta.patch +netfilter-ebtables-move-to-two-stage-removal-scheme.patch +netfilter-ebtables-close-dangling-table-module-init-.patch +netfilter-x_tables-close-dangling-table-module-init-.patch +netfilter-bridge-eb_tables-close-module-init-race.patch +netfilter-nf_conntrack_expect-restore-helper-propaga.patch +kprobes-skip-non-symbol-addresses-in-kprobe_add_ksym.patch +test_kprobes-clear-kprobes-between-test-runs.patch +tcp-fix-imbalanced-icsk_accept_queue-count.patch +net-napi-avoid-gro-timer-misfiring-at-end-of-busypol.patch +net-shaper-reject-reparenting-of-existing-nodes.patch +idpf-fix-read_dev_clk_lock-spinlock-init-in-idpf_ptp.patch +ice-fix-setting-rss-vsi-hash-for-e830.patch +ice-fix-locking-in-ice_dcb_rebuild.patch +ice-dpll-fix-rclk-pin-state-get-for-e810.patch +ice-dpll-fix-misplaced-header-macros.patch +net-lan966x-avoid-unregistering-netdev-on-register-f.patch +net-ti-icssm-prueth-fix-eth_ports_node-leak-in-probe.patch +phy-marvell-mvebu-a3700-utmi-fix-incorrect-usb2_phy_.patch +phy-spacemit-remove-incorrect-clk_disable-in-spacemi.patch +nfsd-fix-infinite-loop-in-layout-state-revocation.patch +asoc-sdw_utils-add-quirk-to-ignore-rt712-codec_mic.patch +asoc-sdw_utils-add-quirk-to-ignore-rt721-codec_mic.patch +fprobe-fix-unregister_fprobe-to-wait-for-rcu-grace-p.patch +fs-statmount-fix-slab-out-of-bounds-write-in-statmou.patch +fs-fix-return-in-jfs_mkdir-and-orangefs_mkdir.patch +irqchip-ath79-cpu-remove-unused-function.patch +fs-fix-forced-iversion-increment-on-lazytime-timesta.patch +ublk-reject-max_sectors-smaller-than-page_sectors-in.patch +nsfs-fix-wrong-error-code-returned-for-pidns-ioctls.patch +irq_work-fix-use-after-free-in-irq_work_single-on-pr.patch +nvme-fix-bio-leak-on-mapping-failure.patch +nvme-pci-fix-use-after-free-in-nvme_free_host_mem.patch +zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch +tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch +asoc-sof-amd-fix-error-code-handling-in-psp_send_cmd.patch +powerpc-82xx-fix-uninitialized-pointers-with-free-at.patch +powerpc-fix-dead-default-for-guest_state_buffer_test.patch +powerpc-hv-gpci-fix-preempt-count-leak-in-sysfs-show.patch +netfs-fix-cancellation-of-a-dio-and-single-read-subr.patch +netfs-fix-missing-locking-around-retry-adding-new-su.patch +netfs-fix-missing-barriers-when-accessing-stream-sub.patch +netfs-fix-netfs_read_to_pagecache-to-pause-on-subreq.patch +netfs-fix-potential-for-tearing-in-remote_i_size-and.patch +netfs-fix-zeropoint-update-where-i_size-remote_i_siz.patch +netfs-fix-vm_bug_on_folio-issue-in-netfs_write_begin.patch +netfs-fix-overrun-check-in-netfs_extract_user_iter.patch +netfs-fix-netfs_invalidate_folio-to-clear-dirty-bit-.patch +netfs-defer-the-emission-of-trace_netfs_folio.patch +netfs-fix-streaming-write-being-overwritten.patch +netfs-fix-potential-deadlock-in-write-through-mode.patch +netfs-fix-read-gaps-to-remove-netfs_folio-from-fille.patch +netfs-fix-write-streaming-disablement-if-fd-open-o_r.patch +netfs-fix-early-put-of-sink-folio-in-netfs_read_gaps.patch +netfs-fix-leak-of-request-in-netfs_write_begin-error.patch +netfs-fix-potential-uaf-in-netfs_unlock_abandoned_re.patch +netfs-fix-partial-invalidation-of-streaming-write-fo.patch +netfs-fix-folio-private-handling-in-netfs_perform_wr.patch +netfs-fix-netfs_read_folio-to-wait-on-writeback.patch +netfs-afs-fix-write-skipping-in-dir-link-writepages.patch +afs-fix-the-locking-used-by-afs_get_link.patch +net-ethernet-cortina-make-rx-skb-per-port.patch +net-ethernet-cortina-drop-half-assembled-skb.patch +net-ethernet-cortina-carry-over-frag-counter.patch +net-ethernet-cs89x0-remove-stale-config_mach_mx31ads.patch +wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch +wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch +wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch +net-shaper-flip-the-polarity-of-the-valid-flag.patch +net-shaper-fix-trivial-ordering-issue-in-net_shaper_.patch +net-shaper-reject-duplicate-leaves-in-group-request.patch +net-shaper-set-ret-to-enomem-when-genlmsg_new-fails-.patch +net-shaper-fix-undersized-reply-skb-allocation-in-gr.patch +net-shaper-reject-handle-ids-exceeding-internal-bit-.patch +net-shaper-enforce-singleton-netdev-scope-with-id-0.patch +net-shaper-reject-queue-scope-handle-with-missing-id.patch +block-don-t-overwrite-bip_vcnt-in-bio_integrity_copy.patch +block-recompute-nr_integrity_segments-in-blk_insert_.patch +hid-quirks-really-enable-the-intended-work-around-fo.patch +block-bio-integrity-fix-null-ptr-deref-in-bio_integr.patch +accel-qaic-add-overflow-check-to-remap_pfn_range-dur.patch +net-smc-avoid-null-deref-of-conn-lnk-in-smc_msg_even.patch +ethtool-fix-ethnl_bitmap32_not_zero-bit-interval-sem.patch +drm-msm-dpu-fix-uv-scanlines-calculation-for-yuv-ubw.patch +drm-msm-dpu-fix-kaanapali-cwb-register-configuration.patch +drm-msm-dsi-don-t-dump-registers-past-the-mapped-reg.patch +drm-msm-dpu-don-t-mix-devm-and-drmm-functions.patch +block-rename-struct-gendisk-zone_wplugs_lock-field.patch +block-allow-submitting-all-zone-writes-from-a-single.patch +block-fix-handling-of-dead-zone-write-plugs.patch +selftests-ublk-cap-nthreads-to-kernel-s-actual-nr_hw.patch +x86-mce-restore-mca-polling-interval-halving.patch +documentation-intel_pstate-fix-description-of-asymme.patch +drm-msm-fix-gmem_base-for-a650.patch +drm-msm-a6xx-add-soft-fuse-detection-support.patch +drm-msm-adreno-fix-a-reference-leak-in-a6xx_gpu_init.patch +drm-msm-adreno-fix-userspace-triggered-crash-on-a2xx.patch +drm-msm-a6xx-restore-sysprof_active.patch +drm-msm-fix-iommu_map_sgtable-return-value-check-and.patch +drm-msm-a6xx-check-kzalloc-return-in-a8xx_hfi_send_p.patch +asoc-intel-sof_sdw-prepare-for-configuration-without.patch +asoc-sdw_utils-cs42l43-allow-spk-component-names-to-.patch +asoc-sdw_utils-check-speaker-component-string-alloca.patch +riscv-docs-fix-unmatched-quote-warning.patch +powerpc-time-remove-redundant-preempt_disable-enable.patch +net-smc-reject-chid-0-accept-that-matches-an-empty-i.patch +net-tls-fix-off-by-one-in-sg_chain-entry-count-for-w.patch +net-tls-prevent-chain-after-chain-in-plain-text-sg.patch +net-phy-dp83tc811-add-reading-of-abilities.patch +ovpn-tcp-use-cached-peer-pointer-in-ovpn_tcp_close.patch +ovpn-respect-peer-refcount-in-cmd_new_peer-error-pat.patch +ovpn-fix-race-between-deleting-interface-and-adding-.patch +cifs-client-stage-smb3_reconfigure-updates-and-resto.patch +phy-apple-atc-fix-typec-switch-mux-leak-on-unbind.patch +gcc-plugins-always-define-const_cast_gimple-and-cons.patch +x86-xen-fix-xen_e820_swap_entry_with_ram.patch +vfio-pci-check-bar-resources-before-exporting-a-dmab.patch +ovpn-disable-bhs-when-updating-device-stats.patch +tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch +net-mlx5-do-not-restore-destination-less-tc-rules.patch +net-mlx5-skip-disabled-vports-when-setting-max-tx-sp.patch +scsi-sd-fix-return-code-handling-in-sd_spinup_disk.patch +asoc-codecs-fs210x-fix-possible-buffer-overflow.patch +iommupt-directly-call-iommupt-s-unmap_range.patch +iommupt-avoid-rewalking-during-map.patch +iommu-fix-loss-of-errno-on-map-failure-for-classic-o.patch +iommu-fix-up-map-unmap-debugging-for-iommupt-domains.patch +iommu-handle-unmap-error-when-iommu_debug-is-enabled.patch +iommupt-check-for-missing-page_size-in-the-pgsize_bi.patch +iommupt-fix-the-end_index-calculation-in-__map_range.patch +alsa-scarlett2-add-missing-error-check-when-initiali.patch +alsa-hda-ca0132-disable-auto-detect-on-manual-output.patch +cachefiles-fix-error-return-when-vfs_mkdir-fails.patch +io_uring-net-punt-ioring_op_bind-async-if-it-needs-f.patch +vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch +btrfs-check-for-subvolume-before-deleting-squota-qgr.patch +btrfs-fix-squota-accounting-during-enable-generation.patch +asoc-amd-acp-sdw-legacy-check-cpu-dai-name-before-lo.patch +spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch +netfilter-nft_inner-release-local_lock-before-re-ena.patch +alsa-hda-realtek-use-alc287_fixup_txnw2781_i2c-for-a.patch +drm-msm-snapshot-fix-dumping-of-the-unaligned-region.patch +hwmon-lm90-stop-work-before-releasing-hwmon-device.patch +hwmon-lm90-add-lock-protection-to-lm90_alert.patch +wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch +wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch +dma-mapping-move-dma_map_resource-sanity-check-into-.patch +drm-gem-make-the-gem-lru-lock-part-of-drm_device.patch +drm-xe-gsc-fix-double-free-of-managed-bo-in-error-pa.patch +drm-xe-vf-fix-signature-of-print-functions.patch +drm-xe-pf-fix-cfi-failure-in-debugfs-access.patch +drm-xe-consolidate-workaround-entries-for-wa_1401998.patch +drm-xe-consolidate-workaround-entries-for-wa_1803385.patch +drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch +drm-xe-tuning-apply-windower-hardware-filtering-sett.patch +drm-xe-define-and-use-mcr-version-of-common_slice_ch.patch-29026 +wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch +wifi-ath12k-fix-eht-tx-mcs-limitation-due-to-wrong-2.patch +drm-mediatek-mtk_hdmi_ddc_v2-fix-non-static-global-v.patch +drm-mediatek-mtk_hdmi_v2-fix-non-static-global-varia.patch +drm-mediatek-mtk_cec-fix-non-static-global-variable.patch +drm-mediatek-mtk_hdmi_ddc-fix-non-static-global-vari.patch +io_uring-propagate-array_index_nospec-opcode-into-re.patch +srcu-don-t-queue-workqueue-handlers-to-never-online-.patch +cgroup-rstat-validate-cpu-before-css_rstat_cpu-acces.patch +net-mlx5e-xsk-fix-unlocked-writing-to-icosq.patch +cifs-fix-undefined-variables.patch +ice-ptp-serialize-e825-phy-timer-start-with-ptp-lock.patch +ice-ptp-use-primary-nac-semaphore-on-e825.patch +igc-set-tx-buffer-type-for-smd-frames.patch +drm-i915-dp-fix-readback-for-target_rr-in-adaptive-s.patch +phy-qcom-qmp-usbc-fix-out-of-bounds-array-access-in-.patch +kbuild-pacman-pkg-make-rc-releases-adhere-to-pacman-.patch +net-dsa-mt7530-fix-fdb-entries-not-aging-out-with-sh.patch +net-dsa-mt7530-preserve-vlan-tags-on-trapped-link-lo.patch +net-mana-fix-toctou-double-fetch-of-hwc_msg_id-from-.patch +platform-surface-aggregator_registry-omit-battery-ac.patch +platform-x86-adv_swbutton-check-acpi_handle-against-.patch +platform-x86-hp_accel-check-acpi_companion-against-n.patch +platform-x86-intel-hid-check-acpi_handle-against-nul.patch +platform-x86-intel_sar-check-acpi_handle-against-nul.patch +platform-x86-intel-vbtn-check-acpi_handle-against-nu.patch +platform-x86-uniwill-laptop-properly-initialize-char.patch +platform-x86-uniwill-laptop-accept-charging-threshol.patch +platform-x86-uniwill-laptop-fix-behavior-of-force-mo.patch +platform-x86-asus-armoury-fix-mini-led-mode-get-set-.patch +asoc-soc-utils-add-missing-va_end-in-snd_soc_ret.patch +drm-amdgpu-align-amdgpu_gtt_mgr-entries-to-tlb-size-.patch +drm-amdgpu-vce1-check-that-the-gpu-address-is-128-mi.patch +drm-amdgpu-vce1-fix-vce-1-firmware-size-and-offsets.patch +rdma-mana_ib-report-max_msg_sz-in-mana_ib_query_port.patch +rdma-rtrs-fix-use-after-free-in-path-file-creation-c.patch +bridge-mcast-fix-a-possible-use-after-free-when-remo.patch +net-phy-honor-eee_disabled_modes-in-phy_support_eee.patch +net-phy-honor-eee_disabled_modes-in-phy_advertise_ee.patch +net-airoha-fix-npu-rx-dma-descriptor-bits.patch +pds_core-fix-error-handling-in-pdsc_devcmd_wait.patch +pds_core-fix-debugfs_lookup-dentry-leak-and-error-ha.patch +erofs-fix-managed-cache-race-for-unaligned-extents.patch +erofs-harden-h_shared_count-in-erofs_init_inode_xatt.patch +erofs-fix-metabuf-leak-in-inode-xattr-initialization.patch +wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch +wifi-mac80211-fix-mle-defragmentation.patch +wifi-mac80211-fix-multi-link-element-inheritance.patch +wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch +alsa-seq-serialize-ump-output-teardown-with-event_in.patch +cgroup-rstat-relax-nmi-guard-after-switch-to-try_cmp.patch +tracing-avoid-null-return-from-hist_field_name-on-tr.patch +bluetooth-hci_sync-fix-not-setting-mask-for-hci_evt_.patch +bluetooth-btintel_pcie-fix-incorrect-mac-access-prog.patch +bluetooth-btmtk-fix-urb-setup_packet-leak-in-error-p.patch +udp-gso-fix-handling-checksum-in-__udp_gso_segment.patch +udp-fix-udp-length-on-last-gso_partial-segment.patch +net-mlx5e-fix-eswitch-mode-block-underflow-on-ipsec-.patch +net-shaper-annotate-the-data-races.patch +net-shaper-rework-the-valid-marking-again.patch +crypto-krb5-rxrpc-fix-lack-of-pre-decrypt-pre-verify.patch +rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to-.patch +net-ag71xx-check-error-for-platform_get_irq.patch +bpf-skmsg-fix-verdict-sk_data_ready-racing-with-ktls.patch +tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch +net-stmmac-eswin-fix-hsp-csr-init-ordering-after-clo.patch +net-stmmac-eswin-clear-txd-and-rxd-delay-registers-d.patch +net-stmmac-eswin-correct-rgmii-delay-granularity-to-.patch +net-stmmac-eswin-validate-rgmii-delay-values.patch +gpio-cdev-check-if-uapi-v2-config-attributes-are-cor.patch +gpio-aggregator-fix-a-potential-use-after-free.patch +gpio-aggregator-stop-using-dev-sync-probe.patch +gpio-aggregator-remove-the-software-node-when-deacti.patch +gpio-aggregator-lock-device-when-calling-device_is_b.patch +asoc-cs35l56-fix-flushing-of-irq-work-in-cs35l56_sdw.patch +drm-xe-oa-fix-exec_queue-leak-on-width-check-in-stre.patch +asoc-cs-amp-lib-fix-wrong-sizeof-in-_cs_amp_set_efi_.patch +asoc-cs-amp-lib-fix-missing-dput-after-debugfs_looku.patch +selftests-net-fix-checksums-in-xdp_native.patch +nvme-pci-fix-dma_vecs-leak-on-p2p-memory.patch +nvme-pci-fix-dma-mapping-leak-on-data-setup-error.patch +octeontx2-af-npc-fix-allmulticast-skip-logic-for-lbk.patch +net-mana-validate-rx_req_idx-to-prevent-out-of-bound.patch +tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch +net-airoha-disable-gdm2-forwarding-before-configurin.patch +pds_core-ensure-null-termination-for-firmware-versio.patch +net-gro-don-t-merge-zcopy-skbs.patch +net-enetc-fix-missing-error-code-when-pf-vf_state-al.patch +io_uring-nop-pass-all-errors-to-userspace.patch +blk-mq-pop-cached-request-if-it-is-usable.patch +ksmbd-fix-durable-reconnect-error-path-file-lifetime.patch +loongarch-kprobes-fix-handling-of-fatal-unrecoverabl.patch +block-avoid-use-after-free-in-disk_free_zone_resourc.patch +documentation-laptops-update-documentation-for-uniwi.patch +platform-x86-uniwill-laptop-do-not-enable-the-chargi.patch diff --git a/queue-7.0/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch b/queue-7.0/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch new file mode 100644 index 0000000000..32cfd08f96 --- /dev/null +++ b/queue-7.0/spi-mtk-snfi-fix-resource-leak-in-mtk_snand_read_pag.patch @@ -0,0 +1,38 @@ +From e86e68f66b10d2d289ad1b027495014e370614f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 01:55:37 +0800 +Subject: spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() + +From: Felix Gu + +[ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] + +When DMA read times out in mtk_snand_read_page_cache(), the original code +erroneously jumped to cleanup label which skips DMA unmapping and ECC +disable, causing a resource leak. + +Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") +Signed-off-by: Felix Gu +Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-mtk-snfi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c +index 73fa84475f0e4..7725748cab2a7 100644 +--- a/drivers/spi/spi-mtk-snfi.c ++++ b/drivers/spi/spi-mtk-snfi.c +@@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, + &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { + dev_err(snf->dev, "DMA timed out for reading from cache.\n"); + ret = -ETIMEDOUT; +- goto cleanup; ++ goto cleanup2; + } + + // Wait for BUS_SEC_CNTR returning expected value +-- +2.53.0 + diff --git a/queue-7.0/srcu-don-t-queue-workqueue-handlers-to-never-online-.patch b/queue-7.0/srcu-don-t-queue-workqueue-handlers-to-never-online-.patch new file mode 100644 index 0000000000..3826eb892b --- /dev/null +++ b/queue-7.0/srcu-don-t-queue-workqueue-handlers-to-never-online-.patch @@ -0,0 +1,73 @@ +From e0a31860c72016fe059edeabf67c1c210c48cd14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 19:54:41 +0200 +Subject: srcu: Don't queue workqueue handlers to never-online CPUs + +From: Paul E. McKenney + +[ Upstream commit 593889c401426004bd0ea0f6d4fcece728b03420 ] + +While an srcu_struct structure is in the midst of switching from CPU-0 +to all-CPUs state, it can attempt to invoke callbacks for CPUs that +have never been online. Worse yet, it can attempt in invoke callbacks +for CPUs that never will be online, even including imaginary CPUs not in +cpu_possible_mask. This can cause hangs on s390, which is not set up to +deal with workqueue handlers being scheduled on such CPUs. This commit +therefore causes Tree SRCU to refrain from queueing workqueue handlers +on CPUs that have not yet (and might never) come online. + +Because callbacks are not invoked on CPUs that have not been +online, it is an error to invoke call_srcu(), synchronize_srcu(), or +synchronize_srcu_expedited() on a CPU that is not yet fully online. +However, it turns out to be less code to redirect the callbacks +from too-early invocations of call_srcu() than to warn about such +invocations. This commit therefore also redirects callbacks queued on +not-yet-fully-online CPUs to the boot CPU. + +Reported-by: Vasily Gorbik +Fixes: 61bbcfb50514 ("srcu: Push srcu_node allocation to GP when non-preemptible") +Signed-off-by: Paul E. McKenney +Tested-by: Vasily Gorbik +Tested-by: Samir +Reviewed-by: Shrikanth Hegde +Cc: Tejun Heo +Signed-off-by: Uladzislau Rezki (Sony) +Signed-off-by: Boqun Feng +Signed-off-by: Sasha Levin +--- + kernel/rcu/srcutree.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c +index 0d01cd8c4b4a7..7c2f7cc131f7a 100644 +--- a/kernel/rcu/srcutree.c ++++ b/kernel/rcu/srcutree.c +@@ -897,11 +897,9 @@ static void srcu_schedule_cbs_snp(struct srcu_struct *ssp, struct srcu_node *snp + { + int cpu; + +- for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) { +- if (!(mask & (1UL << (cpu - snp->grplo)))) +- continue; +- srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, cpu), delay); +- } ++ for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) ++ if ((mask & (1UL << (cpu - snp->grplo))) && rcu_cpu_beenfullyonline(cpu)) ++ srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, cpu), delay); + } + + /* +@@ -1322,7 +1320,9 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp, + */ + idx = __srcu_read_lock_nmisafe(ssp); + ss_state = smp_load_acquire(&ssp->srcu_sup->srcu_size_state); +- if (ss_state < SRCU_SIZE_WAIT_CALL) ++ // If !rcu_cpu_beenfullyonline(), interrupts are still disabled, ++ // so no migration is possible in either direction from this CPU. ++ if (ss_state < SRCU_SIZE_WAIT_CALL || !rcu_cpu_beenfullyonline(raw_smp_processor_id())) + sdp = per_cpu_ptr(ssp->sda, get_boot_cpu_id()); + else + sdp = raw_cpu_ptr(ssp->sda); +-- +2.53.0 + diff --git a/queue-7.0/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch b/queue-7.0/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch new file mode 100644 index 0000000000..bafb89ce42 --- /dev/null +++ b/queue-7.0/tap-fix-stack-info-leak-in-tap_ioctl-siocgifhwaddr.patch @@ -0,0 +1,51 @@ +From 4f139f42f64488b2e0b4b7a1ec07f2d3dfbdf89c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 00:57:38 -0700 +Subject: tap: fix stack info leak in tap_ioctl() SIOCGIFHWADDR + +From: Weiming Shi + +[ Upstream commit bddc09212c24934643bd44fc794748d2bbb3b6cd ] + +In the SIOCGIFHWADDR path, tap_ioctl() copies 16 bytes of an +uninitialised on-stack struct sockaddr_storage to userspace via +ifr_hwaddr, but netif_get_mac_address() only writes sa_family and +dev->addr_len (6 for Ethernet) bytes, leaving sa_data[6..13] uninitialised. + +Those 8 trailing bytes leak kernel stack contents; SIOCGIFHWADDR on a +macvtap chardev returns kernel .text and direct-map pointers, defeating +KASLR. + +Initialise ss at declaration. + +Fixes: 3b23a32a6321 ("net: fix dev_ifsioc_locked() race condition") +Reported-by: Xiang Mei +Signed-off-by: Weiming Shi +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/20260520075736.3415676-3-bestswngs@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/tap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/tap.c b/drivers/net/tap.c +index b8240737dc519..a590e07ce0a98 100644 +--- a/drivers/net/tap.c ++++ b/drivers/net/tap.c +@@ -919,11 +919,11 @@ static long tap_ioctl(struct file *file, unsigned int cmd, + struct tap_queue *q = file->private_data; + struct tap_dev *tap; + void __user *argp = (void __user *)arg; ++ struct sockaddr_storage ss = {}; + struct ifreq __user *ifr = argp; + unsigned int __user *up = argp; + unsigned short u; + int __user *sp = argp; +- struct sockaddr_storage ss; + int s; + int ret; + +-- +2.53.0 + diff --git a/queue-7.0/tcp-fix-imbalanced-icsk_accept_queue-count.patch b/queue-7.0/tcp-fix-imbalanced-icsk_accept_queue-count.patch new file mode 100644 index 0000000000..9032f8999b --- /dev/null +++ b/queue-7.0/tcp-fix-imbalanced-icsk_accept_queue-count.patch @@ -0,0 +1,46 @@ +From 9983c80a05b28bcdb8fad91cd38d42e1722e29c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 03:59:19 +0000 +Subject: tcp: Fix imbalanced icsk_accept_queue count. + +From: Kuniyuki Iwashima + +[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] + +When TCP socket migration happens in reqsk_timer_handler(), +@sk_listener will be updated with the new listener. + +When we call __inet_csk_reqsk_queue_drop(), the listener must +be the one stored in req->rsk_listener. + +The cited commit accidentally replaced oreq->rsk_listener with +sk_listener, leading to imbalanced icsk_accept_queue count. + +Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). + +Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/inet_connection_sock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index bc987a59a0952..f1988fd503540 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1137,7 +1137,7 @@ static void reqsk_timer_handler(struct timer_list *t) + } + + drop: +- __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); ++ __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); + reqsk_put(oreq); + } + +-- +2.53.0 + diff --git a/queue-7.0/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch b/queue-7.0/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch new file mode 100644 index 0000000000..3f00a0a67e --- /dev/null +++ b/queue-7.0/tcp-fix-out-of-bounds-access-for-twsk-in-tcp_ao_esta.patch @@ -0,0 +1,52 @@ +From ee682d49eaae43bccbf9542fbcbcc59006e85554 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 12:08:46 +0000 +Subject: tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key(). + +From: Kuniyuki Iwashima + +[ Upstream commit 03cb001ef87b3f8d859cf7f96329acf3d6235d29 ] + +lockdep_sock_is_held() was added in tcp_ao_established_key() +by the cited commit. + +It can be called from tcp_v[46]_timewait_ack() with twsk. + +Since it does not have sk->sk_lock, the lockdep annotation +results in out-of-bound access. + + $ pahole -C tcp_timewait_sock vmlinux | grep size + /* size: 288, cachelines: 5, members: 8 */ + $ pahole -C sock vmlinux | grep sk_lock + socket_lock_t sk_lock; /* 440 192 */ + +Let's not use lockdep_sock_is_held() for TCP_TIME_WAIT. + +Fixes: 6b2d11e2d8fc ("net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals") +Reported-by: Damiano Melotti +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/tcp_ao.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c +index a97cdf3e6af4c..0a4b38b315fed 100644 +--- a/net/ipv4/tcp_ao.c ++++ b/net/ipv4/tcp_ao.c +@@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, + { + struct tcp_ao_key *key; + +- hlist_for_each_entry_rcu(key, &ao->head, node, lockdep_sock_is_held(sk)) { ++ hlist_for_each_entry_rcu(key, &ao->head, node, ++ sk_fullsock(sk) && lockdep_sock_is_held(sk)) { + if ((sndid >= 0 && key->sndid != sndid) || + (rcvid >= 0 && key->rcvid != rcvid)) + continue; +-- +2.53.0 + diff --git a/queue-7.0/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch b/queue-7.0/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch new file mode 100644 index 0000000000..fb2cf6f8d8 --- /dev/null +++ b/queue-7.0/tcp-fix-stale-per-cpu-tcp_tw_isn-leak-enabling-isn-p.patch @@ -0,0 +1,199 @@ +From 961b8e393c482f4a0dbc5eabf25ec5d39faa3e9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 19 May 2026 08:46:11 +0000 +Subject: tcp: fix stale per-CPU tcp_tw_isn leak enabling ISN prediction + +From: Eric Dumazet + +[ Upstream commit 1bbf0ced1d9db73ac7893c2187f3459288603e0d ] + +Blamed commit moved the TIME_WAIT-derived ISN from the skb control +block to a per-CPU variable, assuming the value would always be consumed +by tcp_conn_request() for the same packet that wrote it. That assumption +is violated by multiple drop paths between the producer +(__this_cpu_write(tcp_tw_isn, isn) in tcp_v{4,6}_rcv()) and the consumer +(tcp_conn_request()): + + - min_ttl / min_hopcount check + - xfrm policy check + - tcp_inbound_hash() MD5/AO mismatch + - tcp_filter() eBPF/SO_ATTACH_FILTER drop + - th->syn && th->fin discard in tcp_rcv_state_process() TCP_LISTEN + - psp_sk_rx_policy_check() in tcp_v{4,6}_do_rcv() + - tcp_checksum_complete() in tcp_v{4,6}_do_rcv() + - tcp_v{4,6}_cookie_check() returning NULL + +When a packet is dropped on any of these paths, tcp_tw_isn is left set. + +The next SYN processed on the same CPU then consumes the non zero value in +tcp_conn_request(), receiving a potentially predictable ISN. + +This patch moves back tcp_tw_isn to skb->cb[], getting rid of the per-cpu +variable. + +Note that tcp_v{4,6}_fill_cb() do not set it. + +Very litle impact on overall code size/complexity: + +$ scripts/bloat-o-meter -t vmlinux.old vmlinux.new +add/remove: 0/0 grow/shrink: 2/1 up/down: 8/-15 (-7) +Function old new delta +tcp_v6_rcv 3038 3042 +4 +tcp_v4_rcv 3035 3039 +4 +tcp_conn_request 2938 2923 -15 +Total: Before=24436060, After=24436053, chg -0.00% + +Fixes: 41eecbd712b7 ("tcp: replace TCP_SKB_CB(skb)->tcp_tw_isn with a per-cpu field") +Reported-by: Chris Mason +Signed-off-by: Eric Dumazet +Reviewed-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260519084611.2485277-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/tcp.h | 7 ++++--- + net/ipv4/tcp.c | 3 --- + net/ipv4/tcp_input.c | 15 ++++++--------- + net/ipv4/tcp_ipv4.c | 3 ++- + net/ipv6/tcp_ipv6.c | 3 ++- + 5 files changed, 14 insertions(+), 17 deletions(-) + +diff --git a/include/net/tcp.h b/include/net/tcp.h +index ebc72dce4134d..e29d73118d82e 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -65,8 +65,6 @@ static inline void tcp_orphan_count_dec(void) + this_cpu_dec(tcp_orphan_count); + } + +-DECLARE_PER_CPU(u32, tcp_tw_isn); +- + void tcp_time_wait(struct sock *sk, int state, int timeo); + + #define MAX_TCP_HEADER L1_CACHE_ALIGN(128 + MAX_HEADER) +@@ -1060,10 +1058,13 @@ struct tcp_skb_cb { + __u32 seq; /* Starting sequence number */ + __u32 end_seq; /* SEQ + FIN + SYN + datalen */ + union { +- /* Note : ++ /* Notes : ++ * tcp_tw_isn is used in input path only ++ * (isn chosen by tcp_timewait_state_process()) + * tcp_gso_segs/size are used in write queue only, + * cf tcp_skb_pcount()/tcp_skb_mss() + */ ++ u32 tcp_tw_isn; + struct { + u16 tcp_gso_segs; + u16 tcp_gso_size; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index cee51749df16c..f27f50111172c 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -299,9 +299,6 @@ enum { + DEFINE_PER_CPU(unsigned int, tcp_orphan_count); + EXPORT_PER_CPU_SYMBOL_GPL(tcp_orphan_count); + +-DEFINE_PER_CPU(u32, tcp_tw_isn); +-EXPORT_PER_CPU_SYMBOL_GPL(tcp_tw_isn); +- + long sysctl_tcp_mem[3] __read_mostly; + EXPORT_IPV6_MOD(sysctl_tcp_mem); + +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index cb4bcc5a85787..a8b626ac1ade1 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -7648,6 +7648,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, + struct sock *sk, struct sk_buff *skb) + { + struct tcp_fastopen_cookie foc = { .len = -1 }; ++ u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn; + struct tcp_options_received tmp_opt; + const struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); +@@ -7658,20 +7659,16 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, + struct dst_entry *dst; + struct flowi fl; + u8 syncookies; +- u32 isn; + + #ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; + #endif + +- isn = __this_cpu_read(tcp_tw_isn); +- if (isn) { +- /* TW buckets are converted to open requests without +- * limitations, they conserve resources and peer is +- * evidently real one. +- */ +- __this_cpu_write(tcp_tw_isn, 0); +- } else { ++ /* If isn is non-zero, this SYN originally matched a TIME_WAIT socket. ++ * TW sockets are converted to open requests without limitations, ++ * we skip the queue limits and syncookie checks in the block below. ++ */ ++ if (!isn) { + syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); + + if (syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) { +diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c +index c7b2463c2e254..0bda739f3d68e 100644 +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -2274,6 +2274,7 @@ int tcp_v4_rcv(struct sk_buff *skb) + } + } + ++ isn = 0; + process: + if (static_branch_unlikely(&ip4_min_ttl)) { + /* min_ttl can be changed concurrently from do_ip_setsockopt() */ +@@ -2302,6 +2303,7 @@ int tcp_v4_rcv(struct sk_buff *skb) + th = (const struct tcphdr *)skb->data; + iph = ip_hdr(skb); + tcp_v4_fill_cb(skb, iph, th); ++ TCP_SKB_CB(skb)->tcp_tw_isn = isn; + + skb->dev = NULL; + +@@ -2387,7 +2389,6 @@ int tcp_v4_rcv(struct sk_buff *skb) + sk = sk2; + tcp_v4_restore_cb(skb); + refcounted = false; +- __this_cpu_write(tcp_tw_isn, isn); + goto process; + } + +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index bb09d5ccf5990..a41ea2a866ee3 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -1818,6 +1818,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + } + } + ++ isn = 0; + process: + if (static_branch_unlikely(&ip6_min_hopcount)) { + /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ +@@ -1846,6 +1847,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + th = (const struct tcphdr *)skb->data; + hdr = ipv6_hdr(skb); + tcp_v6_fill_cb(skb, hdr, th); ++ TCP_SKB_CB(skb)->tcp_tw_isn = isn; + + skb->dev = NULL; + +@@ -1933,7 +1935,6 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) + sk = sk2; + tcp_v6_restore_cb(skb); + refcounted = false; +- __this_cpu_write(tcp_tw_isn, isn); + goto process; + } + +-- +2.53.0 + diff --git a/queue-7.0/test_kprobes-clear-kprobes-between-test-runs.patch b/queue-7.0/test_kprobes-clear-kprobes-between-test-runs.patch new file mode 100644 index 0000000000..7a4de7c2dc --- /dev/null +++ b/queue-7.0/test_kprobes-clear-kprobes-between-test-runs.patch @@ -0,0 +1,128 @@ +From 3eb9b88e65f1f5e1508ed7c1c9fe85cbd12bf72d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:56:36 +0900 +Subject: test_kprobes: clear kprobes between test runs + +From: Martin Kaiser + +[ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ] + +Running the kprobes sanity tests twice makes all tests fail and +eventually crashes the kernel. + +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # Totals: pass:5 fail:0 skip:0 total:5 + ok 1 kprobes_test +[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run +... + # test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 + Expected 0 == register_kprobe(&kp), but + register_kprobe(&kp) == -22 (0xffffffffffffffea) +... + Unable to handle kernel paging request ... + +The testsuite defines several kprobes and kretprobes as static variables +that are preserved across test runs. + +After register_kprobe and unregister_kprobe, a kprobe contains some +leftover data that must be cleared before the kprobe can be registered +again. The tests are setting symbol_name to define the probe location. +Address and flags must be cleared. + +The existing code clears some of the probes between subsequent tests, but +not between two test runs. The leftover data from a previous test run +makes the registrations fail in the next run. + +Move the cleanups for all kprobes into kprobes_test_init, this function +is called before each single test (including the first test of a test +run). + +Link: https://lore.kernel.org/all/20260507134615.1010905-1-martin@kaiser.cx/ + +Fixes: e44e81c5b90f ("kprobes: convert tests to kunit") +Signed-off-by: Martin Kaiser +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + lib/tests/test_kprobes.c | 29 ++++++++++++++++++----------- + 1 file changed, 18 insertions(+), 11 deletions(-) + +diff --git a/lib/tests/test_kprobes.c b/lib/tests/test_kprobes.c +index b7582010125c3..06e729e4de051 100644 +--- a/lib/tests/test_kprobes.c ++++ b/lib/tests/test_kprobes.c +@@ -12,6 +12,12 @@ + + #define div_factor 3 + ++#define KP_CLEAR(_kp) \ ++do { \ ++ (_kp).addr = NULL; \ ++ (_kp).flags = 0; \ ++} while (0) ++ + static u32 rand1, preh_val, posth_val; + static u32 (*target)(u32 value); + static u32 (*recursed_target)(u32 value); +@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) + + current_test = test; + +- /* addr and flags should be cleard for reusing kprobe. */ +- kp.addr = NULL; +- kp.flags = 0; +- + KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); + preh_val = 0; + posth_val = 0; +@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) + struct kretprobe *rps[2] = {&rp, &rp2}; + + current_test = test; +- /* addr and flags should be cleard for reusing kprobe. */ +- rp.kp.addr = NULL; +- rp.kp.flags = 0; + KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); + + krph_val = 0; +@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) + unsigned long myretaddr = (unsigned long)__builtin_return_address(0); + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + /* + * Run the stacktrace_driver() to record correct return address in +@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + struct kretprobe *rps[2] = {&rp3, &rp4}; + + current_test = test; +- rp3.kp.addr = NULL; +- rp3.kp.flags = 0; + + //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); + +@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + + static int kprobes_test_init(struct kunit *test) + { ++ KP_CLEAR(kp); ++ KP_CLEAR(kp2); ++ KP_CLEAR(kp_missed); ++#ifdef CONFIG_KRETPROBES ++ KP_CLEAR(rp.kp); ++ KP_CLEAR(rp2.kp); ++#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE ++ KP_CLEAR(rp3.kp); ++ KP_CLEAR(rp4.kp); ++#endif ++#endif ++ + target = kprobe_target; + target2 = kprobe_target2; + recursed_target = kprobe_recursed_target; +-- +2.53.0 + diff --git a/queue-7.0/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch b/queue-7.0/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch new file mode 100644 index 0000000000..5fb9eb97a5 --- /dev/null +++ b/queue-7.0/tls-preserve-sk_err-across-recvmsg-when-data-has-bee.patch @@ -0,0 +1,125 @@ +From ae4f3179fc5e792c8daeb28bf5282cbc8f6b0f05 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 May 2026 08:58:25 -0400 +Subject: tls: Preserve sk_err across recvmsg() when data has been copied + +From: Chuck Lever + +[ Upstream commit f508262ae9f21fe0e6c0749948b9dc7dd5a62a70 ] + +The sk_err check in tls_rx_rec_wait() consumes the error via +sock_error(), which clears sk_err atomically. When the caller +(tls_sw_recvmsg, tls_sw_splice_read, or tls_sw_read_sock) already +has bytes copied to userspace, it returns those bytes and discards +the error from this call. sk_err is now zero on the socket, so the +next read syscall observes only RCV_SHUTDOWN and reports a clean +EOF instead of the actual error (typically -ECONNRESET). + +The race is reachable when tls_read_flush_backlog()'s periodic +sk_flush_backlog() triggers tcp_reset() in the middle of a +multi-record read. + +Pass a has_copied flag to tls_rx_rec_wait(). When has_copied is +false, consume sk_err via sock_error() as before. When has_copied +is true, report the error from READ_ONCE() but leave sk_err set: +the caller returns the byte count and discards the err from this +call, and the next read syscall surfaces the preserved sk_err. This +mirrors the tcp_recvmsg() preserve-and-surface pattern. + +The decrypt-abort path is unaffected: tls_err_abort() raises +sk_err to EBADMSG after tls_rx_rec_wait() returns, and nothing +on the caller's return path consumes it, so the EBADMSG surfaces +on the next read. + +tls_sw_splice_read() passes has_copied=false: it processes +one record per call, so no bytes have been copied within the +function when tls_rx_rec_wait() runs. A reset that arrives +between iterations of splice_direct_to_actor() (the sendfile() +path) is still consumed by sock_error() in the later call, and the +outer loop returns the prior iterations' byte count and drops the +error. tcp_splice_read() exhibits the same pattern at the iteration +boundary; addressing it belongs at the splice_direct_to_actor() +layer and is out of scope here. + +Fixes: c46b01839f7a ("tls: rx: periodically flush socket backlog") +Suggested-by: Jakub Kicinski +Signed-off-by: Chuck Lever +Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/tls/tls_sw.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c +index 97e02ac7f0086..11a70c10770bb 100644 +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -1366,9 +1366,14 @@ void tls_sw_splice_eof(struct socket *sock) + mutex_unlock(&tls_ctx->tx_lock); + } + ++/* When has_copied is true the caller has already moved bytes to ++ * userspace. Report sk_err but leave it set so the next read ++ * surfaces it instead of a spurious EOF, otherwise sk_err is ++ * consumed via sock_error(). ++ */ + static int + tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, +- bool released) ++ bool released, bool has_copied) + { + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); +@@ -1386,8 +1391,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + if (!sk_psock_queue_empty(psock)) + return 0; + +- if (sk->sk_err) ++ if (sk->sk_err) { ++ if (has_copied) ++ return -READ_ONCE(sk->sk_err); + return sock_error(sk); ++ } + + if (ret < 0) + return ret; +@@ -1423,7 +1431,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, + } + + if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) +- return tls_rx_rec_wait(sk, psock, nonblock, false); ++ return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); + + return 1; + } +@@ -2111,7 +2119,7 @@ int tls_sw_recvmsg(struct sock *sk, + int to_decrypt, chunk; + + err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, +- released); ++ released, !!(decrypted + copied)); + if (err <= 0) { + if (psock) { + chunk = sk_msg_recvmsg(sk, psock, msg, len, +@@ -2298,7 +2306,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, + struct tls_decrypt_arg darg; + + err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, +- true); ++ true, false); + if (err <= 0) + goto splice_read_end; + +@@ -2384,7 +2392,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, + } else { + struct tls_decrypt_arg darg; + +- err = tls_rx_rec_wait(sk, NULL, true, released); ++ err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); + if (err <= 0) + goto read_sock_end; + +-- +2.53.0 + diff --git a/queue-7.0/tracing-avoid-null-return-from-hist_field_name-on-tr.patch b/queue-7.0/tracing-avoid-null-return-from-hist_field_name-on-tr.patch new file mode 100644 index 0000000000..b47338e864 --- /dev/null +++ b/queue-7.0/tracing-avoid-null-return-from-hist_field_name-on-tr.patch @@ -0,0 +1,52 @@ +From 88d75e8be0907571c97f335d12156e4424029e1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 20:57:47 +0100 +Subject: tracing: Avoid NULL return from hist_field_name() on truncation + +From: David Carlier + +[ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] + +hist_field_name() returns "" everywhere except the fully-qualified +VAR_REF/EXPR case, where snprintf() truncation returns NULL early +and bypasses the bottom NULL->"" guard. Callers don't expect NULL: +strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 +and the strcmp() in the sort-key match loop at :4804 both deref it. + +system and event_name are bounded by MAX_EVENT_NAME_LEN, but the +field name on a VAR_REF is kstrdup'd from a histogram variable +name parsed out of the trigger string and has no length cap, so +a long enough var name in a fully qualified reference can reach +the truncation path. + +Keep the length check but leave field_name as "" on overflow. + +Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com +Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_events_hist.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index f9c8a4f078ea0..f8c0e66cc5871 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1366,10 +1366,8 @@ static const char *hist_field_name(struct hist_field *field, + len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", + field->system, field->event_name, + field->name); +- if (len >= sizeof(full_name)) +- return NULL; +- +- field_name = full_name; ++ if (len < sizeof(full_name)) ++ field_name = full_name; + } else + field_name = field->name; + } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) +-- +2.53.0 + diff --git a/queue-7.0/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch b/queue-7.0/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch new file mode 100644 index 0000000000..cb7853b2c3 --- /dev/null +++ b/queue-7.0/ublk-reject-max_sectors-smaller-than-page_sectors-in.patch @@ -0,0 +1,52 @@ +From 21afc1133f8c173b1c0531aed215d52512e201b1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 May 2026 22:48:43 +0800 +Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter + validation + +From: Ming Lei + +[ Upstream commit 1860c2f85922917d8a46f16a6f4bd2298ffa0fb5 ] + +blk_validate_limits() requires max_hw_sectors >= PAGE_SECTORS and fires +a WARN_ON_ONCE if this invariant is violated. ublk_validate_params() +only checked the upper bound of max_sectors against max_io_buf_bytes, +allowing userspace to pass small values (including zero) that trigger +the warning when blk_mq_alloc_disk() is called from +ublk_ctrl_start_dev(). + +Before 494ea040bcb5, ublk used blk_queue_max_hw_sectors() which silently +clamped small values up to PAGE_SECTORS. The conversion to passing +queue_limits directly to blk_mq_alloc_disk() lost that clamping and now +hits blk_validate_limits()'s WARN_ON_ONCE instead. + +Validate that max_sectors is at least PAGE_SECTORS in +ublk_validate_params() so invalid values are rejected early with +-EINVAL instead of reaching the block layer. + +Fixes: 494ea040bcb5 ("ublk: pass queue_limits to blk_mq_alloc_disk") +Signed-off-by: Ming Lei +Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + drivers/block/ublk_drv.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index 0bdb804fca839..e5f4942d99113 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -868,6 +868,9 @@ static int ublk_validate_params(const struct ublk_device *ub) + if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) + return -EINVAL; + ++ if (p->max_sectors < PAGE_SECTORS) ++ return -EINVAL; ++ + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) + return -EINVAL; + } else +-- +2.53.0 + diff --git a/queue-7.0/udp-fix-udp-length-on-last-gso_partial-segment.patch b/queue-7.0/udp-fix-udp-length-on-last-gso_partial-segment.patch new file mode 100644 index 0000000000..9b356a3d94 --- /dev/null +++ b/queue-7.0/udp-fix-udp-length-on-last-gso_partial-segment.patch @@ -0,0 +1,55 @@ +From 9d3019d783370b953a87166703348daf8f70a3dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 09:22:50 +0300 +Subject: udp: Fix UDP length on last GSO_PARTIAL segment + +From: Gal Pressman + +[ Upstream commit 78effd896eee11ac9db9bcbb53e7bbcad96073d7 ] + +Following the cited commit, __udp_gso_segment() writes single MSS length +in the UDP header. +The cited patch doesn't account for the fact that the last segment could +be a GSO skb by itself. This could happen when the size of the packet is +a multiple of MSS, hence the first segment is also the last one (there +is no need for a remainder skb). + +When the post-loop segment is a GSO skb, assign the single MSS length in +the UDP header. + +Fixes: b10b446ce7ad ("udp: gso: Use single MSS length in UDP header for GSO_PARTIAL") +Reported-by: Matthew Schwartz +Closes: https://lore.kernel.org/all/6c3fb15e-711d-4b8d-b152-e03d9b05293f@linux.dev/ +Tested-by: Matthew Schwartz +Reviewed-by: Dragos Tatulea +Signed-off-by: Gal Pressman +Link: https://patch.msgid.link/20260518062250.3019914-3-gal@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/udp_offload.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c +index e831234326c41..9714d40c5b6e9 100644 +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -591,9 +591,12 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, + uh = udp_hdr(seg); + } + +- /* last packet can be partial gso_size, account for that in checksum */ +- newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) + +- seg->data_len); ++ /* Unless skb fits perfectly as GSO_PARTIAL, the trailing ++ * segment may not be full MSS, account for that in the checksum ++ */ ++ if (!skb_is_gso(seg)) ++ newlen = htons(skb_tail_pointer(seg) - ++ skb_transport_header(seg) + seg->data_len); + check = csum16_add(csum16_sub(uh->check, uh->len), newlen); + + uh->len = newlen; +-- +2.53.0 + diff --git a/queue-7.0/udp-gso-fix-handling-checksum-in-__udp_gso_segment.patch b/queue-7.0/udp-gso-fix-handling-checksum-in-__udp_gso_segment.patch new file mode 100644 index 0000000000..69852b8711 --- /dev/null +++ b/queue-7.0/udp-gso-fix-handling-checksum-in-__udp_gso_segment.patch @@ -0,0 +1,78 @@ +From dad8a316e29dbdadc9a5aed13c913139fc5a8fc1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 May 2026 09:22:49 +0300 +Subject: udp: gso: Fix handling checksum in __udp_gso_segment + +From: Alice Mikityanska + +[ Upstream commit 5f17ae0f595aeb560155ce98edbe44d3eacc7e40 ] + +The cited commit started using msslen for uh->len, but still uses newlen +to adjust uh->check. Although the checksum is ignored in most cases due +to the hardware offload, __udp_gso_segment attempts to maintain the +correct one. Fix uh->check and adjust it by the right value. + +Additionally, after the fix, newlen becomes assigned and unused before +the loop. The code can be simplified a bit if mss adjustment is dropped, +so that newlen becomes equal to msslen before the loop, and msslen can +be also dropped, saving a few lines of code. + +This brings us back to one variable, drops an unneeded arithmetic for +mss, and fixes the UDP checksum. + +Fixes: b10b446ce7ad ("udp: gso: Use single MSS length in UDP header for GSO_PARTIAL") +Signed-off-by: Alice Mikityanska +Reviewed-by: Willem de Bruijn +Signed-off-by: Gal Pressman +Link: https://patch.msgid.link/20260518062250.3019914-2-gal@nvidia.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/udp_offload.c | 13 ++----------- + 1 file changed, 2 insertions(+), 11 deletions(-) + +diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c +index 6b1654c1ad4ac..e831234326c41 100644 +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -483,11 +483,11 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, + struct sock *sk = gso_skb->sk; + unsigned int sum_truesize = 0; + struct sk_buff *segs, *seg; +- __be16 newlen, msslen; + struct udphdr *uh; + unsigned int mss; + bool copy_dtor; + __sum16 check; ++ __be16 newlen; + int ret = 0; + + mss = skb_shinfo(gso_skb)->gso_size; +@@ -556,15 +556,6 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, + return segs; + } + +- msslen = htons(sizeof(*uh) + mss); +- +- /* GSO partial and frag_list segmentation only requires splitting +- * the frame into an MSS multiple and possibly a remainder, both +- * cases return a GSO skb. So update the mss now. +- */ +- if (skb_is_gso(segs)) +- mss *= skb_shinfo(segs)->gso_segs; +- + seg = segs; + uh = udp_hdr(seg); + +@@ -587,7 +578,7 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, + if (!seg->next) + break; + +- uh->len = msslen; ++ uh->len = newlen; + uh->check = check; + + if (seg->ip_summed == CHECKSUM_PARTIAL) +-- +2.53.0 + diff --git a/queue-7.0/vfio-pci-check-bar-resources-before-exporting-a-dmab.patch b/queue-7.0/vfio-pci-check-bar-resources-before-exporting-a-dmab.patch new file mode 100644 index 0000000000..4565b4e30d --- /dev/null +++ b/queue-7.0/vfio-pci-check-bar-resources-before-exporting-a-dmab.patch @@ -0,0 +1,46 @@ +From 344f56433b7974199ef59af0564ace811d6b40f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 07:58:24 -0700 +Subject: vfio/pci: Check BAR resources before exporting a DMABUF + +From: Matt Evans + +[ Upstream commit 702809dabdecca807bdd50cfdcc1c980feb2ba62 ] + +A DMABUF exports access to BAR resources and, although they are +requested at startup time, we need to ensure they really were reserved +before exporting. Otherwise, it's possible to access unreserved +resources through the export. + +Add a check to the DMABUF-creation path. + +Fixes: 5d74781ebc86c ("vfio/pci: Add dma-buf export support for MMIO regions") +Signed-off-by: Matt Evans +Link: https://lore.kernel.org/r/20260511145829.2993601-3-mattev@meta.com +Signed-off-by: Alex Williamson +Signed-off-by: Sasha Levin +--- + drivers/vfio/pci/vfio_pci_dmabuf.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c +index b1d658b8f7b51..05385b63e2baf 100644 +--- a/drivers/vfio/pci/vfio_pci_dmabuf.c ++++ b/drivers/vfio/pci/vfio_pci_dmabuf.c +@@ -232,9 +232,11 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, + return -EINVAL; + + /* +- * For PCI the region_index is the BAR number like everything else. ++ * For PCI the region_index is the BAR number like everything ++ * else. Check that PCI resources have been claimed for it. + */ +- if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX) ++ if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX || ++ vfio_pci_core_setup_barmap(vdev, get_dma_buf.region_index)) + return -ENODEV; + + dma_ranges = memdup_array_user(&arg->dma_ranges, get_dma_buf.nr_ranges, +-- +2.53.0 + diff --git a/queue-7.0/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch b/queue-7.0/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch new file mode 100644 index 0000000000..b54e814999 --- /dev/null +++ b/queue-7.0/vsock-virtio-fix-zerocopy-completion-for-multi-skb-s.patch @@ -0,0 +1,168 @@ +From d44580e4dadb4a8acc05cde0eb2ce0238b63059d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 11:29:48 +0200 +Subject: vsock/virtio: fix zerocopy completion for multi-skb sends + +From: Stefano Garzarella + +[ Upstream commit ae38d9179190a956e2a87a69ef1dd6f451b51c4d ] + +When a large message is fragmented into multiple skbs, the zerocopy +uarg is only allocated and attached to the last skb in the loop. +Non-final skbs carry pinned user pages with no completion tracking, +so the kernel has no way to notify userspace when those pages are safe +to reuse. If the loop breaks early the uarg is never allocated at all, +leaking pinned pages with no completion notification. + +Fix this by following the approach used by TCP: allocate the zerocopy +uarg (if not provided by the caller) before the send loop and attach +it to every skb via skb_zcopy_set(), which takes a reference per skb. +Each skb's completion properly decrements the refcount, and the +notification only fires after the last skb is freed. +On failure, if no data was sent, the uarg is cleanly aborted via +net_zcopy_put_abort(). + +This issue was initially discovered by sashiko while reviewing commit +1cb36e252211 ("vsock/virtio: fix MSG_ZEROCOPY pinned-pages accounting") +but was pre-existing. + +Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support") +Closes: https://sashiko.dev/#/patchset/20260420132051.217589-1-sgarzare%40redhat.com +Reported-by: Maher Azzouzi +Signed-off-by: Stefano Garzarella +Acked-by: Michael S. Tsirkin +Acked-by: Arseniy Krasnov +Link: https://patch.msgid.link/20260514092948.268720-1-sgarzare@redhat.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/vmw_vsock/virtio_transport_common.c | 83 ++++++++++--------------- + 1 file changed, 34 insertions(+), 49 deletions(-) + +diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c +index 8bea16dd22407..e8fb2e20db0f3 100644 +--- a/net/vmw_vsock/virtio_transport_common.c ++++ b/net/vmw_vsock/virtio_transport_common.c +@@ -72,34 +72,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops, + return true; + } + +-static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, +- struct sk_buff *skb, +- struct msghdr *msg, +- size_t pkt_len, +- bool zerocopy) +-{ +- struct ubuf_info *uarg; +- +- if (msg->msg_ubuf) { +- uarg = msg->msg_ubuf; +- net_zcopy_get(uarg); +- } else { +- struct ubuf_info_msgzc *uarg_zc; +- +- uarg = msg_zerocopy_realloc(sk_vsock(vsk), +- pkt_len, NULL, false); +- if (!uarg) +- return -1; +- +- uarg_zc = uarg_to_msgzc(uarg); +- uarg_zc->zerocopy = zerocopy ? 1 : 0; +- } +- +- skb_zcopy_init(skb, uarg); +- +- return 0; +-} +- + static int virtio_transport_fill_skb(struct sk_buff *skb, + struct virtio_vsock_pkt_info *info, + size_t len, +@@ -319,8 +291,10 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + u32 src_cid, src_port, dst_cid, dst_port; + const struct virtio_transport *t_ops; + struct virtio_vsock_sock *vvs; ++ struct ubuf_info *uarg = NULL; + u32 pkt_len = info->pkt_len; + bool can_zcopy = false; ++ bool have_uref = false; + u32 rest_len; + int ret; + +@@ -362,6 +336,25 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + if (can_zcopy) + max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE, + (MAX_SKB_FRAGS * PAGE_SIZE)); ++ ++ if (info->msg->msg_flags & MSG_ZEROCOPY && ++ info->op == VIRTIO_VSOCK_OP_RW) { ++ uarg = info->msg->msg_ubuf; ++ ++ if (!uarg) { ++ uarg = msg_zerocopy_realloc(sk_vsock(vsk), ++ pkt_len, NULL, false); ++ if (!uarg) { ++ virtio_transport_put_credit(vvs, pkt_len); ++ return -ENOMEM; ++ } ++ ++ if (!can_zcopy) ++ uarg_to_msgzc(uarg)->zerocopy = 0; ++ ++ have_uref = true; ++ } ++ } + } + + rest_len = pkt_len; +@@ -380,27 +373,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + break; + } + +- /* We process buffer part by part, allocating skb on +- * each iteration. If this is last skb for this buffer +- * and MSG_ZEROCOPY mode is in use - we must allocate +- * completion for the current syscall. +- * +- * Pass pkt_len because msg iter is already consumed +- * by virtio_transport_fill_skb(), so iter->count +- * can not be used for RLIMIT_MEMLOCK pinned-pages +- * accounting done by msg_zerocopy_realloc(). +- */ +- if (info->msg && info->msg->msg_flags & MSG_ZEROCOPY && +- skb_len == rest_len && info->op == VIRTIO_VSOCK_OP_RW) { +- if (virtio_transport_init_zcopy_skb(vsk, skb, +- info->msg, +- pkt_len, +- can_zcopy)) { +- kfree_skb(skb); +- ret = -ENOMEM; +- break; +- } +- } ++ skb_zcopy_set(skb, uarg, NULL); + + virtio_transport_inc_tx_pkt(vvs, skb); + +@@ -424,6 +397,18 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, + + virtio_transport_put_credit(vvs, rest_len); + ++ /* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1. ++ * skb_zcopy_set() increases it for each skb, so we can drop that ++ * initial reference to keep it balanced. ++ */ ++ if (have_uref) { ++ if (rest_len == pkt_len) ++ /* No data sent, abort the notification. */ ++ net_zcopy_put_abort(uarg, true); ++ else ++ net_zcopy_put(uarg); ++ } ++ + /* Return number of bytes, if any data has been sent. */ + if (rest_len != pkt_len) + ret = pkt_len - rest_len; +-- +2.53.0 + diff --git a/queue-7.0/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch b/queue-7.0/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch new file mode 100644 index 0000000000..a4bbf15a84 --- /dev/null +++ b/queue-7.0/wifi-ath10k-skip-wmi-and-beacon-transmission-when-de.patch @@ -0,0 +1,78 @@ +From 74035002d62d2d9bd519b15a833d0593f48dcc55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Apr 2026 14:17:37 +0800 +Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged + +From: Kang Yang + +[ Upstream commit 54a5b38e4396530e5b2f12b54d3844e860ab6784 ] + +In ath10k_wmi_cmd_send(), the current code detects ATH10K_STATE_WEDGED +and sets ret to -ESHUTDOWN, but still proceeds to transmit pending +beacons and calls ath10k_wmi_cmd_send_nowait(). + +This can lead to incorrect behavior, as WMI commands and beacons are +still sent after the device has been marked as wedged, and the original +-ESHUTDOWN return value may be overwritten by the result of the send +path. + +The wedged state indicates the hardware is already unreliable, and no +further interaction with firmware is expected or meaningful in this +state. + +Fix this by skipping beacon transmission and the WMI send path entirely +once ATH10K_STATE_WEDGED is detected, ensuring consistent return values +and avoiding unnecessary firmware interaction. + +Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1 +Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189 + +Fixes: c256a94d1b1b ("wifi: ath10k: shutdown driver when hardware is unreliable") +Signed-off-by: Kang Yang +Reviewed-by: Rameshkumar Sundaram +Reviewed-by: Baochen Qiang +Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath10k/wmi.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c +index 0bdb38edd9152..e57588c19c800 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -3,7 +3,6 @@ + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +@@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) + ret = -ESHUTDOWN; + ath10k_dbg(ar, ATH10K_DBG_WMI, + "drop wmi command %d, hardware is wedged\n", cmd_id); +- } +- /* try to send pending beacons first. they take priority */ +- ath10k_wmi_tx_beacons_nowait(ar); ++ } else { ++ /* try to send pending beacons first. they take priority */ ++ ath10k_wmi_tx_beacons_nowait(ar); + +- ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); +- +- if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) +- ret = -ESHUTDOWN; ++ ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + ++ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) ++ ret = -ESHUTDOWN; ++ } + (ret != -EAGAIN); + }), 3 * HZ); + +-- +2.53.0 + diff --git a/queue-7.0/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch b/queue-7.0/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch new file mode 100644 index 0000000000..b80e7a5baa --- /dev/null +++ b/queue-7.0/wifi-ath11k-fix-error-path-leak-in-ath11k_tm_cmd_wmi.patch @@ -0,0 +1,39 @@ +From 9ae5293a4c622e72817c2a0e0eaf37b3fa1ec7f0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:40 +0200 +Subject: wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm() + +From: Nicolas Escande + +[ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ] + +This is similar to what was fixed by previous patches. We have a call +to ath11k_wmi_cmd_send() which does check the return value, but forgot +to free the related skb on error. + +Fixes: b43310e44edc ("wifi: ath11k: factory test mode support") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/testmode.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c +index a9751ea2a0b73..c72eed358f6dd 100644 +--- a/drivers/net/wireless/ath/ath11k/testmode.c ++++ b/drivers/net/wireless/ath/ath11k/testmode.c +@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret); ++ dev_kfree_skb(skb); + goto out; + } + +-- +2.53.0 + diff --git a/queue-7.0/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch b/queue-7.0/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch new file mode 100644 index 0000000000..445b067add --- /dev/null +++ b/queue-7.0/wifi-ath11k-fix-error-path-leaks-in-some-wmi-wow-cal.patch @@ -0,0 +1,77 @@ +From fa5ca08f49a325180906aee420b9f8f4a2097dd6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 May 2026 15:42:38 +0200 +Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls + +From: Nicolas Escande + +[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] + +Fix two instances where we used to directly return the result of +ath11k_wmi_cmd_send(...). Because we did not check the return value, we +also did not free the skb in the error path. + +Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") +Signed-off-by: Nicolas Escande +Reviewed-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c +index 40747fba3b0c0..024c2aad9fb4e 100644 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -9332,6 +9332,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + struct wmi_wow_host_wakeup_ind *cmd; + struct sk_buff *skb; + size_t len; ++ int ret; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9345,14 +9346,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_wow_enable(struct ath11k *ar) + { + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; +- int len; ++ int ret, len; + + len = sizeof(*cmd); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +@@ -9367,7 +9374,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) + cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n"); + +- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; + } + + int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +-- +2.53.0 + diff --git a/queue-7.0/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch b/queue-7.0/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch new file mode 100644 index 0000000000..5d2e66b020 --- /dev/null +++ b/queue-7.0/wifi-ath11k-fix-peer-resolution-on-rx-path-when-peer.patch @@ -0,0 +1,73 @@ +From 12857262b62c11b4d0331c92b29e5fbdb365e64e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:50:35 +0100 +Subject: wifi: ath11k: fix peer resolution on rx path when peer_id=0 + +From: Matthew Leach + +[ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] + +It has been observed that on certain chipsets a peer can be assigned +peer_id=0. For reception of non-aggregated MPDUs this is fine as +ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer +based upon the source MAC address. On an aggregated link, the mpdu_start +header is only populated by hardware on the first sub-MSDU. This causes +the peer resolution to be skipped for the subsequent MSDUs and the +encryption type of these frames to be set to an incorrect value, +resulting in these MSDUs being dropped by ieee80211. + +ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 +ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 + +Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and +ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID +of 0 to be resolved. + +Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 + +Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") +Reviewed-by: Baochen Qiang +Signed-off-by: Matthew Leach +Reviewed-by: P Praneesh +Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- + drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c +index 85defe11750d5..9bbafbd696e6b 100644 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2214,8 +2214,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) + + lockdep_assert_held(&ab->base_lock); + +- if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + + if (peer) + return peer; +diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c +index 753bd93f02123..51e0840bc0d1e 100644 +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; +- u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); +- if (peer_id) +- ppdu_info->peer_id = peer_id; ++ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +-- +2.53.0 + diff --git a/queue-7.0/wifi-ath12k-fix-eht-tx-mcs-limitation-due-to-wrong-2.patch b/queue-7.0/wifi-ath12k-fix-eht-tx-mcs-limitation-due-to-wrong-2.patch new file mode 100644 index 0000000000..72e6b410f5 --- /dev/null +++ b/queue-7.0/wifi-ath12k-fix-eht-tx-mcs-limitation-due-to-wrong-2.patch @@ -0,0 +1,88 @@ +From bfd948eb53854164a35d83cb7f1065fd40904f76 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 May 2026 11:32:51 +0800 +Subject: wifi: ath12k: fix EHT TX MCS limitation due to wrong 20 MHz-only + parsing + +From: Baochen Qiang + +[ Upstream commit 60fb2cf51e77bb1c0261160b4be44209d68956b1 ] + +When connecting to an AP configured for EHT 20 MHz with a full EHT +MCS/NSS map (supporting MCS 0-13) + +Supported EHT-MCS and NSS Set + EHT-MCS Map (BW <= 80MHz): 0x444444 + .... .... .... .... .... 0100 = Rx Max Nss That Supports EHT-MCS 0-9: 4 + .... .... .... .... 0100 .... = Tx Max Nss That Supports EHT-MCS 0-9: 4 + .... .... .... 0100 .... .... = Rx Max Nss That Supports EHT-MCS 10-11: 4 + .... .... 0100 .... .... .... = Tx Max Nss That Supports EHT-MCS 10-11: 4 + .... 0100 .... .... .... .... = Rx Max Nss That Supports EHT-MCS 12-13: 4 + 0100 .... .... .... .... .... = Tx Max Nss That Supports EHT-MCS 12-13: 4 + +TX throughput is observed to be significantly lower than expected. +Investigation shows that TX rates are limited to EHT MCS 11, even though +the AP advertises support for EHT MCS 12/13. + +The root cause is an incorrect parsing of the Supported EHT-MCS and NSS +Set element in ath12k_peer_assoc_h_eht(). + +IEEE Std 802.11be-2024 Figure 9-1074as describes the format for 20 +MHz-Only Non-AP STAs. + +IEEE Std 802.11be-2024 Figure 9-1074at describes the format for all +other AP and non-AP STAs. + +Currently the first format is parsed when the peer advertises no wider +HE channel width support, without considering whether it is an AP or a +non-AP STA. This is incorrect: the peer AP's capabilities must be parsed +using Figure 9-1074at even when it operates on 20 MHz only. Parsing it +as Figure 9-1074as causes rx_tx_mcs13_max_nss to be interpreted as zero, +which is then passed to firmware, leading firmware to assume the peer +does not support MCS 13 and to limit TX rates at MCS 11. + +Fix this by parsing the Figure 9-1074as format only when the peer is a +20 MHz-Only non-AP STA, i.e. when the local interface operates as AP or +mesh point. + +Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 + +Fixes: 6c95151e2e77 ("wifi: ath12k: Add EHT MCS/NSS rates to Peer Assoc") +Signed-off-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260514-ath12k-fix-20mhz-only-mcs-map-v1-1-a38d4a9b21a2@oss.qualcomm.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath12k/mac.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c +index fa36e984c74b2..6869e9860776d 100644 +--- a/drivers/net/wireless/ath/ath12k/mac.c ++++ b/drivers/net/wireless/ath/ath12k/mac.c +@@ -3446,7 +3446,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, + arg->peer_eht_mcs_count++; + fallthrough; + default: +- if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & ++ if ((vif->type == NL80211_IFTYPE_AP || ++ vif->type == NL80211_IFTYPE_MESH_POINT) && ++ !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz; + +@@ -3475,7 +3477,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, + arg->punct_bitmap = ~arvif->punct_bitmap; + arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15; + +- if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & ++ if ((vif->type == NL80211_IFTYPE_AP || ++ vif->type == NL80211_IFTYPE_MESH_POINT) && ++ !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + if (bw_20->rx_tx_mcs13_max_nss) + max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss, +-- +2.53.0 + diff --git a/queue-7.0/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch b/queue-7.0/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch new file mode 100644 index 0000000000..a5c20e2b6a --- /dev/null +++ b/queue-7.0/wifi-iwlwifi-mld-don-t-dereference-a-pointer-before-.patch @@ -0,0 +1,71 @@ +From ef6fa40f324cca13dc20e871ef2f7371c6e0a62e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 15:14:56 +0300 +Subject: wifi: iwlwifi: mld: don't dereference a pointer before NULL checking + it + +From: Miri Korenblit + +[ Upstream commit d733ed481fd20a8e7bfe5119c4e77761ba3f87ee ] + +In iwl_mld_remove_link, the link->fw_id is saved at the beginning of the +function so we have it after we freed the link. + +But the link pointer can be NULL, and is not checked when the fw_id is +stored. + +Fix it by simply freeing the link at the end of the function. + +fFixes: 0e66a39f4f0e ("wifi: iwlwifi: fix potential use after free in iwl_mld_remove_link()") +Reviewed-by: Johannes Berg +Link: https://patch.msgid.link/20260515151351.371f40fc6711.I6a82cfe9655564e9c5731af91c36493b26b1208e@changeid +Signed-off-by: Miri Korenblit +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mld/link.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c +index b5430e8a73d66..7496528e85874 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c ++++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + /* +- * Copyright (C) 2024-2025 Intel Corporation ++ * Copyright (C) 2024-2026 Intel Corporation + */ + + #include "constants.h" +@@ -504,7 +504,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = link == &mld_vif->deflink; +- u8 fw_id = link->fw_id; + + if (WARN_ON(!link || link->active)) + return; +@@ -512,15 +511,15 @@ void iwl_mld_remove_link(struct iwl_mld *mld, + iwl_mld_rm_link_from_fw(mld, bss_conf); + /* Continue cleanup on failure */ + +- if (!is_deflink) +- kfree_rcu(link, rcu_head); +- + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + +- if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links)) ++ if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) + return; + +- RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL); ++ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); ++ ++ if (!is_deflink) ++ kfree_rcu(link, rcu_head); + } + + void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, +-- +2.53.0 + diff --git a/queue-7.0/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch b/queue-7.0/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch new file mode 100644 index 0000000000..6fd64701f0 --- /dev/null +++ b/queue-7.0/wifi-iwlwifi-mld-fix-tso-segmentation-explosion-when.patch @@ -0,0 +1,76 @@ +From 90c6a276f69b176c08a7d1f8ee5399fa6d2b7c04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 4 Apr 2026 22:41:44 -0700 +Subject: wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is + disabled + +From: Cole Leavitt + +[ Upstream commit 92cee08dc4f00e77fd1317e4343c5d458b0abab7 ] + +When the TLC notification disables AMSDU for a TID, the MLD driver sets +max_tid_amsdu_len to the sentinel value 1. The TSO segmentation path in +iwl_mld_tx_tso_segment() checks for zero but not for this sentinel, +allowing it to reach the num_subframes calculation: + + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad) + = (1 + 2) / (1534 + 2) = 0 + +This zero propagates to iwl_tx_tso_segment() which sets: + + gso_size = num_subframes * mss = 0 + +Calling skb_gso_segment() with gso_size=0 creates over 32000 tiny +segments from a single GSO skb. This floods the TX ring with ~1024 +micro-frames (the rest are purged), creating a massive burst of TX +completion events that can lead to memory corruption and a subsequent +use-after-free in TCP's retransmit queue (refcount underflow in +tcp_shifted_skb, NULL deref in tcp_rack_detect_loss). + +The MVM driver is immune because it checks mvmsta->amsdu_enabled before +reaching the num_subframes calculation. The MLD driver has no equivalent +bitmap check and relies solely on max_tid_amsdu_len, which does not +catch the sentinel value. + +Fix this by detecting the sentinel value (max_tid_amsdu_len == 1) at the +existing check and falling back to non-AMSDU TSO segmentation. Also add +a WARN_ON_ONCE guard after the num_subframes division as defense-in-depth +to catch any future code paths that produce zero through a different +mechanism. + +Suggested-by: Miriam Rachel Korenblit +Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") +Signed-off-by: Cole Leavitt +Link: https://patch.msgid.link/20260405054145.1064152-3-cole@unwrap.rs +Signed-off-by: Miri Korenblit +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mld/tx.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c +index 0d2d059ac4e3e..0bcb1ae694687 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c ++++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c +@@ -834,7 +834,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + return -EINVAL; + + max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid]; +- if (!max_tid_amsdu_len) ++ if (!max_tid_amsdu_len || max_tid_amsdu_len == 1) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ +@@ -846,6 +846,9 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + */ + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad); + ++ if (WARN_ON_ONCE(!num_subframes)) ++ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); ++ + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; +-- +2.53.0 + diff --git a/queue-7.0/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch b/queue-7.0/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch new file mode 100644 index 0000000000..83058e4fb3 --- /dev/null +++ b/queue-7.0/wifi-mac80211-bounds-check-link_id-in-ieee80211_ml_e.patch @@ -0,0 +1,55 @@ +From f68ee43e14c4936aaf1379970066b0cd0efbeb35 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 15 May 2026 12:29:08 +0200 +Subject: wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs + +From: Alexandru Hossu + +[ Upstream commit f718506edd2d9c6a308ded9d13c632bf7b7d5a2c ] + +IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID is 0x000f, so link_id extracted +from a PRIO_ACCESS ML element PER_STA_PROFILE subelement can be 0..15. +sdata->link[] has IEEE80211_MLD_MAX_NUM_LINKS (15) entries (indices 0..14), +making index 15 out-of-bounds. + +A connected WiFi 7 AP can trigger this by sending an EPCS Enable Response +action frame with a PER_STA_PROFILE subelement where link_id = 15. The +unsolicited-notification path (dialog_token = 0) is reachable any time +EPCS is already enabled, without any prior client request. + +sdata->link[15] reads into the first word of sdata->activate_links_work +(a wiphy_work whose embedded list_head is non-NULL after INIT_LIST_HEAD), +so the NULL check on the result does not catch the invalid access. The +garbage pointer is then passed to ieee80211_sta_wmm_params(), which +dereferences link->sdata and crashes the kernel. + +The same class of bug was fixed for ieee80211_ml_reconfiguration() by +commit 162d331d833d ("wifi: mac80211: bounds-check link_id in +ieee80211_ml_reconfiguration"). + +Fixes: de86c5f60839 ("wifi: mac80211: Add support for EPCS configuration") +Signed-off-by: Alexandru Hossu +Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/mlme.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c +index 991519ea44827..eba890366e9fe 100644 +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -11137,6 +11137,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, + control = get_unaligned_le16(pos); + link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID; + ++ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) ++ continue; ++ + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; +-- +2.53.0 + diff --git a/queue-7.0/wifi-mac80211-fix-mle-defragmentation.patch b/queue-7.0/wifi-mac80211-fix-mle-defragmentation.patch new file mode 100644 index 0000000000..42f592d1b6 --- /dev/null +++ b/queue-7.0/wifi-mac80211-fix-mle-defragmentation.patch @@ -0,0 +1,160 @@ +From cd2d33e57921f1d74e534dd6e6c1a7d33eeeefcb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:10:31 +0200 +Subject: wifi: mac80211: fix MLE defragmentation + +From: Johannes Berg + +[ Upstream commit a74e893f30db64cdce0fc7a96d3baa417bcd55f5 ] + +If either reconf or EPCS multi-link element (MLE) is contained in +a non-transmitted profile, the defragmentation routine is called +with a pointer to the defragmented copy, but the original elements. + +This is incorrect for two reasons: + - if the original defragmentation was needed, it will not find the + correct data + - if the original frame is at a higher address, the parsing will + potentially overrun the heap data (though given the layout of + the buffers, only into the new defragmentation buffer, and then + it has to stop and fail once that's filled with copied data. + +Fix it by tracking the container along with the pointer and in +doing so also unify the two almost identical defragmentation +routines. + +Fixes: 4d70e9c5488d ("wifi: mac80211: defragment reconfiguration MLE when parsing") +Reviewed-by: Miriam Rachel Korenblit +Reviewed-by: Ilan Peer +Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/parse.c | 71 +++++++++++++++++++------------------------- + 1 file changed, 31 insertions(+), 40 deletions(-) + +diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c +index 2b3632c6008af..666cdd5fd0ea5 100644 +--- a/net/mac80211/parse.c ++++ b/net/mac80211/parse.c +@@ -34,6 +34,13 @@ + #include "led.h" + #include "wep.h" + ++struct ieee80211_elem_defrag { ++ const struct element *elem; ++ /* container start/len */ ++ const u8 *start; ++ size_t len; ++}; ++ + struct ieee80211_elems_parse { + /* must be first for kfree to work */ + struct ieee802_11_elems elems; +@@ -41,11 +48,7 @@ struct ieee80211_elems_parse { + /* The basic Multi-Link element in the original elements */ + const struct element *ml_basic_elem; + +- /* The reconfiguration Multi-Link element in the original elements */ +- const struct element *ml_reconf_elem; +- +- /* The EPCS Multi-Link element in the original elements */ +- const struct element *ml_epcs_elem; ++ struct ieee80211_elem_defrag ml_reconf, ml_epcs; + + bool multi_link_inner; + bool skip_vendor; +@@ -162,10 +165,14 @@ ieee80211_parse_extension_element(u32 *crc, + } + break; + case IEEE80211_ML_CONTROL_TYPE_RECONF: +- elems_parse->ml_reconf_elem = elem; ++ elems_parse->ml_reconf.elem = elem; ++ elems_parse->ml_reconf.start = params->start; ++ elems_parse->ml_reconf.len = params->len; + break; + case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: +- elems_parse->ml_epcs_elem = elem; ++ elems_parse->ml_epcs.elem = elem; ++ elems_parse->ml_epcs.start = params->start; ++ elems_parse->ml_epcs.len = params->len; + break; + default: + break; +@@ -990,46 +997,27 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, + sub->start, sub->len); + } + +-static void +-ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse) +-{ +- struct ieee802_11_elems *elems = &elems_parse->elems; +- ssize_t ml_len; +- +- ml_len = cfg80211_defragment_element(elems_parse->ml_reconf_elem, +- elems->ie_start, +- elems->total_len, +- elems_parse->scratch_pos, +- elems_parse->scratch + +- elems_parse->scratch_len - +- elems_parse->scratch_pos, +- WLAN_EID_FRAGMENT); +- if (ml_len < 0) +- return; +- elems->ml_reconf = (void *)elems_parse->scratch_pos; +- elems->ml_reconf_len = ml_len; +- elems_parse->scratch_pos += ml_len; +-} +- +-static void +-ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse) ++static const void * ++ieee80211_mle_defrag(struct ieee80211_elems_parse *elems_parse, ++ struct ieee80211_elem_defrag *defrag, ++ size_t *out_len) + { +- struct ieee802_11_elems *elems = &elems_parse->elems; ++ const void *ret; + ssize_t ml_len; + +- ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem, +- elems->ie_start, +- elems->total_len, ++ ml_len = cfg80211_defragment_element(defrag->elem, ++ defrag->start, defrag->len, + elems_parse->scratch_pos, + elems_parse->scratch + + elems_parse->scratch_len - + elems_parse->scratch_pos, + WLAN_EID_FRAGMENT); + if (ml_len < 0) +- return; +- elems->ml_epcs = (void *)elems_parse->scratch_pos; +- elems->ml_epcs_len = ml_len; ++ return NULL; ++ ret = elems_parse->scratch_pos; ++ *out_len = ml_len; + elems_parse->scratch_pos += ml_len; ++ return ret; + } + + struct ieee802_11_elems * +@@ -1109,9 +1097,12 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); + } + +- ieee80211_mle_defrag_reconf(elems_parse); +- +- ieee80211_mle_defrag_epcs(elems_parse); ++ elems->ml_reconf = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_reconf, ++ &elems->ml_reconf_len); ++ elems->ml_epcs = ieee80211_mle_defrag(elems_parse, ++ &elems_parse->ml_epcs, ++ &elems->ml_epcs_len); + + if (elems->tim && !elems->parse_error) { + const struct ieee80211_tim_ie *tim_ie = elems->tim; +-- +2.53.0 + diff --git a/queue-7.0/wifi-mac80211-fix-multi-link-element-inheritance.patch b/queue-7.0/wifi-mac80211-fix-multi-link-element-inheritance.patch new file mode 100644 index 0000000000..6a6babc570 --- /dev/null +++ b/queue-7.0/wifi-mac80211-fix-multi-link-element-inheritance.patch @@ -0,0 +1,112 @@ +From 700912ba9e9a58141bac3a7d111d7140bcb52342 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 8 May 2026 09:10:32 +0200 +Subject: wifi: mac80211: fix multi-link element inheritance + +From: Johannes Berg + +[ Upstream commit fe2d61a5d2849ee75dd4deeb2fe35f78d80721f8 ] + +When parsing a beacon, mac80211 erroneously inherits any +reconfiguration or EPCS multi-link elements from the outer +elements into the multi-BSSID profile that's requested, if +connected to a non-transmitted BSS, unless that profile +has a non-inheritance element. + +This also happens if parsing a multi-BSSID profile that +doesn't have a non-inheritance element. + +Fix this by having an empty non-inheritance element so +cfg80211_is_element_inherited() is invoked in these cases +and causes the parser to skip the elements that should +never be inherited. + +Fixes: cf36cdef10e2 ("wifi: mac80211: Add support for parsing Reconfiguration Multi Link element") +Fixes: 24711d60f849 ("wifi: mac80211: Support parsing EPCS ML element") +Reviewed-by: Ilan Peer +Reviewed-by: Benjamin Berg +Link: https://patch.msgid.link/20260508091032.92184c0a3f08.I3c43b0b63d2cef8a4ddddaef1c2faaeb1de711ad@changeid +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/parse.c | 36 +++++++++++++++++++++++++++++++++--- + 1 file changed, 33 insertions(+), 3 deletions(-) + +diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c +index 666cdd5fd0ea5..77894d9971135 100644 +--- a/net/mac80211/parse.c ++++ b/net/mac80211/parse.c +@@ -34,6 +34,15 @@ + #include "led.h" + #include "wep.h" + ++static const u8 empty_non_inheritance[] = { ++ WLAN_EID_EXTENSION, 1, WLAN_EID_EXT_NON_INHERITANCE, ++ /* ++ * cfg80211_is_element_inherited() hardcodes elements that ++ * cannot be inherited, so we just need an empty one to be ++ * calling it at all. ++ */ ++}; ++ + struct ieee80211_elem_defrag { + const struct element *elem; + /* container start/len */ +@@ -923,7 +932,7 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, + { + struct ieee802_11_elems *elems = &elems_parse->elems; + struct ieee80211_mle_per_sta_profile *prof; +- const struct element *tmp; ++ const struct element *tmp, *ret; + ssize_t ml_len; + const u8 *end; + +@@ -993,8 +1002,17 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, + sub->from_ap = params->from_ap; + sub->link_id = -1; + +- return cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, +- sub->start, sub->len); ++ ret = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, ++ sub->start, sub->len); ++ if (ret) ++ return ret; ++ ++ /* ++ * Since we know we want and found a profile, apply an empty ++ * non-inheritance if the profile didn't have one, so that any ++ * element that shouldn't be inherited by spec isn't. ++ */ ++ return (const void *)empty_non_inheritance; + } + + static const void * +@@ -1030,6 +1048,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + size_t scratch_len = 3 * params->len; + bool multi_link_inner = false; + ++ BUILD_BUG_ON(sizeof(empty_non_inheritance) != empty_non_inheritance[1] + 2); + BUILD_BUG_ON(offsetof(typeof(*elems_parse), elems) != 0); + + /* cannot parse for both a specific link and non-transmitted BSS */ +@@ -1077,6 +1096,17 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + + non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, + sub.start, nontx_len); ++ /* ++ * If it's a non-transmitted BSS, we shouldn't pick ++ * any elements in the outer parsing that shouldn't ++ * be inherited. If the profile has a non-inheritance ++ * element this automatically happens, but if not then ++ * provide an empty one so that the hard-coded elements ++ * in cfg80211_is_element_inherited() are ignored, but ++ * it must be called. ++ */ ++ if (params->bss->transmitted_bss && !non_inherit) ++ non_inherit = (const void *)empty_non_inheritance; + } else { + /* must always parse to get elems_parse->ml_basic_elem */ + non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params, +-- +2.53.0 + diff --git a/queue-7.0/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch b/queue-7.0/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch new file mode 100644 index 0000000000..d13f74efb3 --- /dev/null +++ b/queue-7.0/wifi-wilc1000-fix-dma_buffer-leak-on-bus-acquire-fai.patch @@ -0,0 +1,51 @@ +From 12284f270d799614e54b15fba220b8b783cbe743 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 11 May 2026 09:57:32 +0530 +Subject: wifi: wilc1000: fix dma_buffer leak on bus acquire failure + +From: Shitalkumar Gandhi + +[ Upstream commit dd7b6a8671939708cc4b7a46786d8c11297e8f69 ] + +wilc_wlan_firmware_download() allocates dma_buffer with kmalloc() at +the top of the function and uses a 'fail:' label to free it via +kfree(dma_buffer) on error. + +All later error paths correctly use 'goto fail' to route through this +cleanup. However, the early failure path after the first acquire_bus() +call uses a bare 'return ret;', which leaks dma_buffer whenever the bus +acquire fails. + +Replace the early return with goto fail so the existing cleanup path +runs. + +Found via a custom Coccinelle semantic patch hunting for kmalloc'd +locals leaked on early-return error paths in driver firmware-download +code. + +Fixes: 1241c5650ff7 ("wifi: wilc1000: Fill in missing error handling") +Signed-off-by: Shitalkumar Gandhi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260511042732.998311-1-shitalkumar.gandhi@cambiumnetworks.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/microchip/wilc1000/wlan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c +index 3fa8592eb2503..4b116fe6f9ea9 100644 +--- a/drivers/net/wireless/microchip/wilc1000/wlan.c ++++ b/drivers/net/wireless/microchip/wilc1000/wlan.c +@@ -1265,7 +1265,7 @@ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, + + ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); + if (ret) +- return ret; ++ goto fail; + + wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); + reg &= ~BIT(10); +-- +2.53.0 + diff --git a/queue-7.0/x86-mce-restore-mca-polling-interval-halving.patch b/queue-7.0/x86-mce-restore-mca-polling-interval-halving.patch new file mode 100644 index 0000000000..bcea7fcc33 --- /dev/null +++ b/queue-7.0/x86-mce-restore-mca-polling-interval-halving.patch @@ -0,0 +1,138 @@ +From d51eab7abd3bb24bafdc61c131557f44df434866 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 16 Mar 2026 16:12:00 +0100 +Subject: x86/mce: Restore MCA polling interval halving + +From: Borislav Petkov (AMD) + +[ Upstream commit ea324444ece9f301b5c4ff71b258cc68990c4d61 ] + +RongQing reported that the MCA polling interval doesn't halve when an +error gets logged. It was traced down to the commit in Fixes:, because: + + mce_timer_fn() + |-> mce_poll_banks() + |-> machine_check_poll() + |-> mce_log() + +which will queue the work and return. + +Now, back in mce_timer_fn(): + + /* + * Alert userspace if needed. If we logged an MCE, reduce the polling + * interval, otherwise increase the polling interval. + */ + if (mce_notify_irq()) + +<--- here we haven't ran the notifier chain yet so mce_need_notify is +not set yet so this won't hit and we won't halve the interval iv. + +Now the notifier chain runs. mce_early_notifier() sets the bit, does +mce_notify_irq(), that clears the bit and then the notifier chain +a little later logs the error. + +So this is a silly timing issue. + +But, that's all unnecessary. + +All it needs to happen here is, the "should we notify of a logged MCE" +mce_notify_irq() asks, should be simply a question to the mce gen pool: +"Are you empty?" + +And that then turns into a simple yes or no answer and it all +JustWorks(tm). + +So do that and also distribute the functionality where it belongs: + - Print that MCE events have been logged in mce_log() + - Trigger the mcelog tool specific work in the first notifier + +As a result, mce_notify_irq() can go now. + +Fixes: 011d82611172 ("RAS: Add a Corrected Errors Collector") +Reported-by: Li RongQing +Signed-off-by: Borislav Petkov (AMD) +Reviewed-by: Qiuxu Zhuo +Tested-by: Qiuxu Zhuo +Link: https://lore.kernel.org/r/20260112082747.2842-1-lirongqing@baidu.com +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/cpu/mce/core.c | 33 +++++---------------------------- + 1 file changed, 5 insertions(+), 28 deletions(-) + +diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c +index 8dd424ac5de8a..f3a793e3a6c8f 100644 +--- a/arch/x86/kernel/cpu/mce/core.c ++++ b/arch/x86/kernel/cpu/mce/core.c +@@ -90,7 +90,6 @@ struct mca_config mca_cfg __read_mostly = { + }; + + static DEFINE_PER_CPU(struct mce_hw_err, hw_errs_seen); +-static unsigned long mce_need_notify; + + /* + * MCA banks polled by the period polling timer for corrected events. +@@ -152,8 +151,10 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm); + + void mce_log(struct mce_hw_err *err) + { +- if (mce_gen_pool_add(err)) ++ if (mce_gen_pool_add(err)) { ++ pr_info(HW_ERR "Machine check events logged\n"); + irq_work_queue(&mce_irq_work); ++ } + } + EXPORT_SYMBOL_GPL(mce_log); + +@@ -585,28 +586,6 @@ bool mce_is_correctable(struct mce *m) + } + EXPORT_SYMBOL_GPL(mce_is_correctable); + +-/* +- * Notify the user(s) about new machine check events. +- * Can be called from interrupt context, but not from machine check/NMI +- * context. +- */ +-static bool mce_notify_irq(void) +-{ +- /* Not more than two messages every minute */ +- static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); +- +- if (test_and_clear_bit(0, &mce_need_notify)) { +- mce_work_trigger(); +- +- if (__ratelimit(&ratelimit)) +- pr_info(HW_ERR "Machine check events logged\n"); +- +- return true; +- } +- +- return false; +-} +- + static int mce_early_notifier(struct notifier_block *nb, unsigned long val, + void *data) + { +@@ -618,9 +597,7 @@ static int mce_early_notifier(struct notifier_block *nb, unsigned long val, + /* Emit the trace record: */ + trace_mce_record(err); + +- set_bit(0, &mce_need_notify); +- +- mce_notify_irq(); ++ mce_work_trigger(); + + return NOTIFY_DONE; + } +@@ -1804,7 +1781,7 @@ static void mce_timer_fn(struct timer_list *t) + * Alert userspace if needed. If we logged an MCE, reduce the polling + * interval, otherwise increase the polling interval. + */ +- if (mce_notify_irq()) ++ if (!mce_gen_pool_empty()) + iv = max(iv / 2, (unsigned long) HZ/100); + else + iv = min(iv * 2, round_jiffies_relative(check_interval * HZ)); +-- +2.53.0 + diff --git a/queue-7.0/x86-xen-fix-xen_e820_swap_entry_with_ram.patch b/queue-7.0/x86-xen-fix-xen_e820_swap_entry_with_ram.patch new file mode 100644 index 0000000000..e994ebbdb2 --- /dev/null +++ b/queue-7.0/x86-xen-fix-xen_e820_swap_entry_with_ram.patch @@ -0,0 +1,39 @@ +From fdb7567d368b87a0877d1d5c19addd968dbf0cd1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 May 2026 12:24:17 +0200 +Subject: x86/xen: Fix xen_e820_swap_entry_with_ram() + +From: Juergen Gross + +[ Upstream commit 28e03f78e69cf6628b81f24777799778528a84c1 ] + +When swapping a not page-aligned E820 map entry with RAM, the start +address of the modified entry is calculated wrong (the offset into the +page is subtracted instead of being added to the page address). + +Fixes: be35d91c8880 ("xen: tolerate ACPI NVS memory overlapping with Xen allocated memory") +Reported-by: Jan Beulich +Reviewed-by: Jan Beulich +Signed-off-by: Juergen Gross +Message-ID: <20260505102417.208138-1-jgross@suse.com> +Signed-off-by: Sasha Levin +--- + arch/x86/xen/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c +index ac8021c3a997e..d4738e03a63a4 100644 +--- a/arch/x86/xen/setup.c ++++ b/arch/x86/xen/setup.c +@@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) + /* Fill new entry (keep size and page offset). */ + entry->type = swap_entry->type; + entry->addr = entry_end - swap_size + +- swap_addr - swap_entry->addr; ++ swap_entry->addr - swap_addr; + entry->size = swap_entry->size; + + /* Convert old entry to RAM, align to pages. */ +-- +2.53.0 + diff --git a/queue-7.0/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch b/queue-7.0/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch new file mode 100644 index 0000000000..564cb10ed3 --- /dev/null +++ b/queue-7.0/zonefs-handle-integer-overflow-in-zonefs_fname_to_fn.patch @@ -0,0 +1,51 @@ +From 5209ed8a3464d8382a9e46c070be297c4926b3c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Apr 2026 22:58:15 +0200 +Subject: zonefs: handle integer overflow in zonefs_fname_to_fno + +From: Johannes Thumshirn + +[ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ] + +In zonefs the file name in one of the two directories corresponds to the +zone number. + +Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), +where the parsing of the zone number from the file name can overflow the +'long' data type. + +Add a check for integer overflows and if the fno 'long' did overflow +return -ENOENT. + +Reported-by: Alexey Dobriyan +Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed") +Signed-off-by: Johannes Thumshirn +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + fs/zonefs/super.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c +index e83b2ec5e49f8..d0976c874b74b 100644 +--- a/fs/zonefs/super.c ++++ b/fs/zonefs/super.c +@@ -610,10 +610,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname) + return c - '0'; + + for (i = 0, rname = name + len - 1; i < len; i++, rname--) { ++ long digit; ++ + c = *rname; + if (!isdigit(c)) + return -ENOENT; +- fno += (c - '0') * shift; ++ digit = (c - '0') * shift; ++ if (check_add_overflow(fno, digit, &fno)) ++ return -ENOENT; + shift *= 10; + } + +-- +2.53.0 +