--- /dev/null
+From 78b6e2f61b0fc23c98d504b29c9b77701ee4782b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a73ae9047870e4b30ba2d371b33d1763677d1b9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e0c31cb2fe30aa89af84d557e8983fcfe0f47b16 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b6dca7311a781bce78e791bad90dcf88da6b6a57 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <andy.shevchenko@gmail.com>
+
+[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ]
+
+Use the mem_is_zero() helper where possible.
+
+Signed-off-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/gpio/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+-#include <linux/kernel.h>
+ #include <linux/kfifo.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/poll.h>
+ #include <linux/spinlock.h>
++#include <linux/string.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+@@ -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
+
--- /dev/null
+From 474270bddda7ccf685f0369d1f5abc70345c2374 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7e561bb36a55ed2dd462ecf104c5839de13472dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a13cdc97327de0502ef6b15e31823c952746bc3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 23:03:28 -0400
+Subject: ipv6: route: Unregister netdevice notifier on BPF init failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ 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 <dbgh9129@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b360fa5d41cb8b8c665a27fad5deade506ae69a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 022d6439e9c6e1244f0590f22d9a23a9b43237a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From 4b3d4042bd67d4d07e68e09f0f8ee43034214e17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f616728494cacce73faef93c2300c7011f5bd2e5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 88df69222154a515e57b4389ab41dc7ac6551487 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 02b46ed4b4516ea0928f2570d9c4e41711e35c94 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9ff241312a9ebe43012a897266afb97791f952c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eeed9b7983f1f3b5d11b63fe4f16b5ab16664d96 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b376d2b50e3e4ae3461c0357bcdb461d85b6743e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 59444baf844b89964cf2e0d0093049a04d173364 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 98259646c4b48ab42ad83a32a878317256321570 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e11997a50b3262d6436ca7302a6ba7af6bc11963 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 864a93a86dad7d78c0dec81bea706593597bfff4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jani.nikula@intel.com>
+
+[ 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 <kees@kernel.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4a3d135627c2cf2bdf09633a01ad77e1f18bd78f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f41d23c53a0e029b93c790034bc0fb1602037c8e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5aed049c33a8f571c00040a8389c4121b36e5815 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3d315fc3805d976b6d4a3c5fcb9d5aa00aaacee9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dd17527fc96f2ce1400a8dace1efc0c6aa84aef5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 1cc14017eb4b03567d57db16a579d23b7bbc6b69 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ca4f0ca06c0e65a406f110d07b14ee258b555fb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dffc2212da84678367264abb1854538267f43d72 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 472d992fe9c8671a32f917ee745118e93348165b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5a7aa32abce2242deec2b91503e9e85e48059952 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <andy.shevchenko@gmail.com>
+
+[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ]
+
+Use the mem_is_zero() helper where possible.
+
+Signed-off-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/gpio/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+-#include <linux/kernel.h>
+ #include <linux/kfifo.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/poll.h>
+ #include <linux/spinlock.h>
++#include <linux/string.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+@@ -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
+
--- /dev/null
+From 560f3c14185c26090fd6b652a3c0c716fd67ee28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2d17010257fc5c523895545bef500ad4db01c674 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f68c7c1bd867898911025a2e73427a100cf49c53 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 23:03:28 -0400
+Subject: ipv6: route: Unregister netdevice notifier on BPF init failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ 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 <dbgh9129@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d561bdd966cee9cb3c1d746c38bb2d611ab71c15 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ba94db47a0c89c4cce4145bf4b2b3c694e3f1b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From 427aaefb505d01e651f288b8ca3a61a6e3bcf8ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 54200a739b058d02fd020c1562b68c9e53ecd177 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ed797798e1d033f3a1008a1c5501942e131b1b55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Oct 2025 16:45:37 +0200
+Subject: net: bridge: Flush multicast groups when snooping is disabled
+
+From: Petr Machata <petrm@nvidia.com>
+
+[ 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 <petrm@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 40eedc94f205225e46f3d505ab2e082bc52afa6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3b2b5c941170db0119f48bbdd6bac5c28df2b72b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From daad5d06bdaac438999e78f9254b2ca8061e5e86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <arinc.unal@arinc9.com>
+
+[ 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 <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b5b636fb21157ca6ca53c3db25d064b07bcd2abe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 22 Apr 2025 04:10:20 +0100
+Subject: net: dsa: mt7530: sync driver-specific behavior of MT7531 variants
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Reviewed-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/89ed7ec6d4fa0395ac53ad2809742bb1ce61ed12.1745290867.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e824e40d0e84 ("net: dsa: mt7530: fix FDB entries not aging out with short timeout")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9c9f0a8e7c838801c591a2d6cbbc86a71d83a6bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a87701aa72922c4bdcb2049836d51f5f3e93323d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c470065fda6b69bb45b30571da1c945ad1c0f7de Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e81737519b0efb6ed95562b8bcb10e8fe726045d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 73dba20cbe14d490a919e48915380212224d01fe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From d7743dc2d4bcf152a65eab80676c9abb16996ce9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ef8b2f35ee1f5eb1637709b39467efaef0fca6d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 052bb59c61665f3a0d7c405b2402bf4b212372a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f307f70bbbf56368343e011e2c3a141d8548ef50 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 Oct 2021 17:15:12 +0200
+Subject: netfilter: arp_tables: allow use of arpt_do_table as hookfn
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit e8d225b6002673366abc2e40e30c991bdc8d62ca ]
+
+This is possible now that the xt_table structure is passed in via *priv.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <net/compat.h>
+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
+
--- /dev/null
+From 1f1b596c154d38f81561118087a37754d66453be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Jan 2024 16:42:48 +0100
+Subject: netfilter: arptables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Reviewed-by: Phil Sutter <phil@nwl.cc>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dd443750c3981759c31944f3d8beb0ada5df4f0b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 Mar 2024 21:15:52 -0700
+Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building
+ arp_tables.c
+
+From: Kuniyuki Iwashima <kuniyu@amazon.com>
+
+[ 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:
+ <TASK>
+ __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
+ </TASK>
+
+Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds")
+Reported-by: syzkaller <syzkaller@googlegroups.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 76719a23d5c96d4690773efa9826f16243e7fcf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4b30a7c2d72444a5dd148c1d12a20ff29aab15bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:12 +0100
+Subject: netfilter: ebtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a7e863cc557ce860b44115390ed12d114f47ea0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ef22989140ba8137a7d5f9add8a21de6ca6eabfb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f36d8452557e5a33aa0f8d50e331733865ad14c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jun 2025 17:44:23 +0200
+Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT.
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ 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 <fw@strlen.de>
+Co-developed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5efc00fb9fb380446b8c97f514aa19916dcee2b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Sep 2024 02:58:54 -0700
+Subject: netfilter: Make legacy configs user selectable
+
+From: Breno Leitao <leitao@debian.org>
+
+[ 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 <leitao@debian.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ae03afb432750e922150888d09a18044ba580ea6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 287a51bf65c0fd8401bdacbb6faa6f63237e1d42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ac710563c6967337787a1827e1fa9ad3bf169ce5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3ee29420e4e1f869606efb8da60dabf7285792a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:11 +0100
+Subject: netfilter: xtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 68db07bb9db862eff5c2e6e80b9cfbb8422819dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 6 Feb 2024 14:55:53 +0100
+Subject: netfilter: xtables: fix up kconfig dependencies
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <lkp@intel.com>
+Reported-by: Randy Dunlap <rdunlap@infradead.org>
+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 <rdunlap@infradead.org>
+Tested-by: Randy Dunlap <rdunlap@infradead.org> # build-tested
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 14eece03f0b740daec6c7dcbd79b480695155f79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 930a02989b297e8453692d120d6654cbff3dce9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3c4c6f6c200c3cc8d1fabdc3cb376d7cb6c4169b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 97dd382d9874ef7cd44a106946bec5a779454909 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6c889a6c29fe9d650be2299984fdd6e9f1e49c59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 79441ac01f1da4d19f7b27528b4ebcc1daa3335e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 63aebd04a31e98fc886733a84842ca6ed2d89e17 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jani.nikula@intel.com>
+
+[ 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 <kees@kernel.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7710310f0bc583065521f7fb04106a3b869c2e45 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a111460efde9116bf0c2ed01838f0156075c000e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cfc45b34b20363df43f573a7961cb1c5a226a355 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dfb39e0e87fbdce3a245bdfb820ad961fd5ae90d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a0cc8e6e2e5f78255a348b86b8e46af3a5788cf9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eb82903a66895f4ef4d2e7273d85a74aa6d70b70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 20:21:38 +0300
+Subject: drm/msm/dsi: don't dump registers past the mapped region
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 77c1db3834c8db1d58625a462ea6079725d5cf91 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d307e05589c08a3105cfd185eef7d87d8f62a302 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 2fbb45423a4460e9766d1f8adc3798dcca3a0e10 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 05d6a18b65047d9a27f25172088c3f28db660b4d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8855907a2dbd76aaad80708cc34ca5f8c1641720 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 53a1e843a2cf9dec10f247a95e54c3182650696f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b198a742cdc3415a01f0d74572a279a8ae2dc9db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <andy.shevchenko@gmail.com>
+
+[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ]
+
+Use the mem_is_zero() helper where possible.
+
+Signed-off-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/hte.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+-#include <linux/kernel.h>
+ #include <linux/kfifo.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+@@ -25,6 +24,7 @@
+ #include <linux/rbtree.h>
+ #include <linux/seq_file.h>
+ #include <linux/spinlock.h>
++#include <linux/string.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+@@ -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
+
--- /dev/null
+From 80538df6baa0c6ed726af240ce09e4f60a0c122c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5faf58ffa5858737bbc1ab7eb63d7e31637fc1e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 10:33:16 +0200
+Subject: HID: uclogic: Fix regression of input name assignment
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3a95a2c98d980ce540b5daf335c4bd426269f26 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 98f1089e6738a38bd28887e8d41079089b694e79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 23:03:28 -0400
+Subject: ipv6: route: Unregister netdevice notifier on BPF init failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ 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 <dbgh9129@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f17ce49db1bd9c14b418f2ffb8c32e549cde67e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jiayuan.chen@linux.dev>
+
+[ 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 <bigeasy@linutronix.de>
+Suggested-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1898b4f24f29be4b2d95632ec3dbb6112b23bdd8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a0fd9ec456e18e4e0dadb32381e531efd8bb3e71 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From 195420606b34377dc5d02cab0de2e86ee15d6f06 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 10cb33d1edcc19ff6d65b4b2a588929b0d47e87b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1f375ce4124cfd261472d9fa93b5f4206c52bc8c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Oct 2025 16:45:37 +0200
+Subject: net: bridge: Flush multicast groups when snooping is disabled
+
+From: Petr Machata <petrm@nvidia.com>
+
+[ 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 <petrm@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0b5f5fabbab2d8de4585db610e8ec779ba7658c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4b9cd544efa55adf79361969f3de983b2a0af747 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2673abc7cd1f0d95b458df6754af664df850c88e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <arinc.unal@arinc9.com>
+
+[ 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 <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 114a8c9e6abaaa563c328a8d778fd1222b642858 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 22 Apr 2025 04:10:20 +0100
+Subject: net: dsa: mt7530: sync driver-specific behavior of MT7531 variants
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Reviewed-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/89ed7ec6d4fa0395ac53ad2809742bb1ce61ed12.1745290867.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e824e40d0e84 ("net: dsa: mt7530: fix FDB entries not aging out with short timeout")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 226b1597ab6e1936a97dc53e06b698b9d0000d4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4af4c8a07ac94213807e6e896c6bfa20fa6f21aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fbd195ed75826e57f330ccbc5e88eaecc1088807 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5fcd86ece88e0b0578aac698823074be2643af97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3540d0cc761b7019288bf7d0c94bc64d81f8b28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 21:43:11 +0900
+Subject: net: lan966x: avoid unregistering netdev on register failure
+
+From: Myeonghun Pak <mhun512@gmail.com>
+
+[ 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 <ae878000@gmail.com>
+Signed-off-by: Ijae Kim <ae878000@gmail.com>
+Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
+Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3bb5f55c0a6e67725f2688baf419763e864e8105 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From dab1b251ee1c1123d7a61deb0bf06887fb83ebf0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3f7333267feecc9ae39d4391f6c2e0c762a773e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
+Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 78003da406a337a628fa4db252ece25c67eeb409 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 91ae169f2f2dbe91a105dd671b5f904330a4cb9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3d447b665c5196b4e4fa63f4bc0c536e48bf9fe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5445ac1e1d917b8e8c4bca0c138dbf70539618f4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Jan 2024 16:42:48 +0100
+Subject: netfilter: arptables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Reviewed-by: Phil Sutter <phil@nwl.cc>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e2b622c702013ca0d80dfd33ec3283eec78d8b63 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 Mar 2024 21:15:52 -0700
+Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building
+ arp_tables.c
+
+From: Kuniyuki Iwashima <kuniyu@amazon.com>
+
+[ 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:
+ <TASK>
+ __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
+ </TASK>
+
+Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds")
+Reported-by: syzkaller <syzkaller@googlegroups.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d19344d6078be196205ad30274c76ac47c05283f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 31b08e7485e3b49baf686a56324396a6debd1582 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:12 +0100
+Subject: netfilter: ebtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4cabc0abf23050af4a2eb00a4347489cc6ea2c3f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 332bde2e4549c55bae7b469a718babdc55c54e13 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4fd8ba08d22ba9f7b09e67331fa5c68365b38589 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jun 2025 17:44:23 +0200
+Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT.
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ 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 <fw@strlen.de>
+Co-developed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b2399c7be634fdaa657a6398be0b0c86b1bdde0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Sep 2024 02:58:54 -0700
+Subject: netfilter: Make legacy configs user selectable
+
+From: Breno Leitao <leitao@debian.org>
+
+[ 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 <leitao@debian.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 52fa5ccd0b1247175261438baa604793b079444f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 850ceb93c8b61020afb1780cf3ae25b0b52193d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4f5d0768119e450f16a20a8836c38e4fd6369d98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:20 +0200
+Subject: netfilter: x_tables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2319b33ffcf04844bcf0dfc375fca7ab689b0e6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1618cdd6bf14d5034f5987795aab22ed27c42637 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:11 +0100
+Subject: netfilter: xtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3419026d7dccb97b25c126794f3efbf78b9f6d18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 6 Feb 2024 14:55:53 +0100
+Subject: netfilter: xtables: fix up kconfig dependencies
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <lkp@intel.com>
+Reported-by: Randy Dunlap <rdunlap@infradead.org>
+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 <rdunlap@infradead.org>
+Tested-by: Randy Dunlap <rdunlap@infradead.org> # build-tested
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e8dda57749952ef717e511bb3617b35fd6da5327 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3f829d076e43af5d2728415537d63900cad75895 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:44:58 +0530
+Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for
+ sm8150
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 934e41eb596db83a9a8b11792f773acb4fd98953 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9519ed6f6f30a272c5c6ec02c9f1860d2031d213 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4483cdce6aa2784b5094338649d972ef5c2627c7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8aa8cdf419ceddd3acc4daadf6e7fcdd16e540db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 185dd234f0071113f7359a595ba8a17933a01eb2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sayalip@linux.ibm.com>
+
+[ 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 <SF,ME,RI,LE> 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 <mahesh@linux.ibm.com>
+Acked-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
+[Maddy: Fixed the commit title]
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9b9c6b8dd15a7619d811a61bcbfa010c8199d0bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From cff4caa91653d6e4794c0677a258c17f24cd41c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 35743bca68d8d12836b4449ad3a2020834a125c7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jani.nikula@intel.com>
+
+[ 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 <kees@kernel.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 62fdbb7d219b9348e24dee5750e6d787f193f158 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 41a7a9138706473769840bda1e803d89f3123b25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 80204bcc44fd7bee969cc8bae0eace13e9a1eeb0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6cfd59deec4a78cd2af72e5020784bebffd9a98e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 43315ba8e0289cd7bd3a15e53946cb80eb5f2262 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.leach@collabora.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
+Reviewed-by: P Praneesh <praneesh.p@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 924a5b4064a1baff8c1ecca78eb9b443e0b06615 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Mar 2023 16:57:00 +0200
+Subject: wifi: ath11k: fix rssi station dump not updated in QCN9074
+
+From: P Praneesh <quic_ppranees@quicinc.com>
+
+[ 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 <quic_ppranees@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7643f5ff026094ac8bb18f00845d90d33a919e10 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Dec 2022 23:37:14 +0200
+Subject: wifi: ath11k: initialize hw_ops for IPQ5018
+
+From: Sriram R <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From db48fa0e8ef2406a01446bb10789ba49a644564a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Dec 2022 23:37:14 +0200
+Subject: wifi: ath11k: remap ce register space for IPQ5018
+
+From: Sriram R <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0099f986135714443d926066e5518a70a682d0ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 4 Nov 2022 14:24:03 +0530
+Subject: wifi: ath11k: Trigger sta disconnect on hardware restart
+
+From: Youghandhar Chintala <quic_youghand@quicinc.com>
+
+[ 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 <lkp@intel.com>
+
+Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1
+
+Signed-off-by: Youghandhar Chintala <quic_youghand@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8328fd26c015fa9f168a8978094252bd7b8f6757 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Dec 2022 23:37:14 +0200
+Subject: wifi: ath11k: update ce configurations for IPQ5018
+
+From: Sriram R <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a6e0710d52b9da9984e6e9ab00c17b3666f2c675 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Dec 2022 23:37:14 +0200
+Subject: wifi: ath11k: update hal srng regs for IPQ5018
+
+From: Sriram R <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2e0968fc81bbb45a0b671ea2104768a695a70745 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Dec 2022 23:37:13 +0200
+Subject: wifi: ath11k: update hw params for IPQ5018
+
+From: Sriram R <quic_srirrama@quicinc.com>
+
+[ 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 <quic_srirrama@quicinc.com>
+Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ea2b96ebf39e38c225ecc081f9184d19088b8d73 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 12:39:01 -0700
+Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap
+
+From: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+
+[ 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 <lukas.maar@tugraz.at>
+Fixes: ff13be830333 ("accel/qaic: Add datapath")
+Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+[jhugo: fix braces from checkpatch --strict]
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9e122e421f7c58d99b444282d2db2b4c96440a72 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:12:38 +0800
+Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5278cc6f99764158e07430ad600b9cd6c75b84b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:01:39 +0800
+Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 68d9498099058a6c74cbdab20666ba8357a1b8e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 10:39:14 +0700
+Subject: ALSA: scarlett2: Add missing error check when initialise Autogain
+ Status
+
+From: Robertus Diawan Chris <robertusdchris@gmail.com>
+
+[ 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 <robertusdchris@gmail.com>
+Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1f3616f67d39f16ee2e6e4ab74ec72221a61da9d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:32:49 +0800
+Subject: ALSA: seq: Serialize UMP output teardown with event_input
+
+From: Zhang Cen <rollkingzzc@gmail.com>
+
+[ 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 <tiwai@suse.de>
+
+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 <rollkingzzc@gmail.com>
+Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 24bf10d832471b63011826debae3e7e73021c78d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:10 +0100
+Subject: ARM: dts: renesas: genmai: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0992052c1db4d7a6bffa43a069a3a159011f2e24 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:11 +0100
+Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7a3af1a56f3ed7ceaa3c57f191143f36a1901426 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ed75a483951fd436115ad2b197d642eb10235442 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:30:57 +0100
+Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove()
+
+From: Richard Fitzgerald <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5690c8c77a8259052708f47fa401e4ef6bcae0cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Sep 2025 12:33:16 -0700
+Subject: blk-integrity: enable p2p source and destination
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <hch@lst.de>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9b6e897af3a5bd258b6660caee81eb567293d0ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 16 Oct 2024 13:13:09 -0700
+Subject: blk-integrity: remove seed for user mapped buffers
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <kbusch@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
+Reviewed-by: Kanchan Joshi <joshi.k@samsung.com>
+Link: https://lore.kernel.org/r/20241016201309.1090320-1-kbusch@meta.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 637ad3a56a3b ("block: don't overwrite bip_vcnt in bio_integrity_copy_user()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bcdad12583153fdfa3752142a5e540d1ce21e4d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 Aug 2025 07:12:57 -0700
+Subject: blk-integrity: use simpler alignment check
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <kbusch@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f1405c7cc535726df5fafe7c30913fd28b076b6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <iam@sung-woo.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 <cshi008@fiu.edu>
+Acked-by: Weidong Zhu <weizhu@fiu.edu>
+Acked-by: Dave Tian <daveti@purdue.edu>
+Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+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 <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5bef07db158f5ff44b72465827e6208569b5e677 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 22:51:51 +0100
+Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user()
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2a4f486970ac0fbddde1b4a886923e2701b12a8b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 3 Jun 2025 12:31:32 -0600
+Subject: block: drop direction param from bio_integrity_copy_user()
+
+From: Caleb Sander Mateos <csander@purestorage.com>
+
+[ 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 <csander@purestorage.com>
+Reviewed-by: Keith Busch <kbusch@kernel.org>
+Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
+Link: https://lore.kernel.org/r/20250603183133.1178062-1-csander@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 01addf13679f46fe50e191e39e2f230bacc5f3ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <anuj20.g@samsung.com>
+
+[ 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 <anuj20.g@samsung.com>
+Signed-off-by: Kanchan Joshi <joshi.k@samsung.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Keith Busch <kbusch@kernel.org>
+Link: https://lore.kernel.org/r/20241128112240.8867-4-anuj20.g@samsung.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bf36f8c52fff797debc10f6d06c02f5fae322125 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 15:22:30 -0600
+Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request
+
+From: Casey Chen <cachen@purestorage.com>
+
+[ 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
+<linux/blk-integrity.h>, 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 <cachen@purestorage.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f1069804a02e5ba1a4fafa13cde749b88a7b7883 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:24:02 +0800
+Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths
+
+From: Jiajia Liu <liujiajia@kylinos.cn>
+
+[ 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 <liujiajia@kylinos.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5cea6c486d42cea2978eddabd5d5077f56a9c9cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 May 2026 23:56:26 +0900
+Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx
+
+From: Xingwang Xiang <v3rdant.xiang@gmail.com>
+
+[ 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 <v3rdant.xiang@gmail.com>
+Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a417c9ac7cad030e98196f454df0100538923a70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3d4961fb5b1bf67625738f227d87d8c24a2adf04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 19:53:46 -0700
+Subject: btrfs: fix squota accounting during enable generation
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3462fa3a0e49eca9a54118084e8430f08d62b74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <fdmanana@suse.com>
+
+[ 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] <TASK>
+ [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 <boris@bur.io>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 74dc7f7347d9edd16b24f99f15b8de383f78d875 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ankit.k.nautiyal@intel.com>
+
+[ 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 <mitulkumar.ajitkumar.golani@intel.com>
+Cc: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com
+(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c0026c9a730bea621e23a95e48ab7dce23732d09 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 20:21:38 +0300
+Subject: drm/msm/dsi: don't dump registers past the mapped region
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b7ba8f82f3365c56b079c40450bd08625144f7e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a843751ccca02753bfe064712d9f6cb07cc10d54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From f652db0b8bce4fda044a560621dcea94eb6bf21c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <daniele.ceraolospurio@intel.com>
+Assisted-by: Claude:claude-opus-4.6
+Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c24333634ba827f7f5a252604d84e4b3940b164a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <ashutosh.dixit@intel.com>
+Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a587a6370bd8be5a4c98407845ed822f09e7ff5a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 23:19:18 +0530
+Subject: drm/xe/pf: Fix CFI failure in debugfs access
+
+From: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+
+[ 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 <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com
+(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5e02f1675e087146277b8f75e973bd5c73053c4f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 17:57:26 +0200
+Subject: drm/xe/vf: Fix signature of print functions
+
+From: Michal Wajdeczko <michal.wajdeczko@intel.com>
+
+[ 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 <michal.wajdeczko@intel.com>
+Cc: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Shuicheng Lin <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com
+(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 53d0bfec51ca42b51a42bc9337ab461c2293694d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 27cc9472499928ccef77b1969e21a37ac8c68c7e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:33 +0100
+Subject: firmware: arm_ffa: Align RxTx buffer size before mapping
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sebastianene@google.com>
+Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com
+Reviewed-by: Sebastian Ene <sebastianene@google.com>
+Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org
+Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7faa0338e46c75f2962ed6dcd7c1e4faaf6c851f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@arm.com>
+
+[ 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 <viresh.kumar@linaro.org>
+Message-Id: <20250217-ffa_updates-v3-18-bd1d9de615e7@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ce710f6b9da6cf26d441fbbed4bdb5ac87b1b341 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:30 +0100
+Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8129bf2937a99dfdb77a68173b144c0cb6fdfaa4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b2e6055b1976439f447ebf68c1f41cfd2aa89145 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@arm.com>
+
+[ 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 <viresh.kumar@linaro.org>
+Message-Id: <20250217-ffa_updates-v3-5-bd1d9de615e7@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Stable-dep-of: 3974ea193840 ("firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 632371eb23fae1c6de641d1f05932921cbd31a71 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:28 +0100
+Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in
+ workqueue
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9dc0e1e130a36bd3aff57c9c08ec479f50cddae2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:35 +0100
+Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ef63f66b79720f7ec18c222322bf8757d2d5ed86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 17 Feb 2025 15:38:47 +0000
+Subject: firmware: arm_ffa: Refactor addition of partition information into
+ XArray
+
+From: Viresh Kumar <viresh.kumar@linaro.org>
+
+[ 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 <viresh.kumar@linaro.org>
+Message-Id: <20250217-ffa_updates-v3-6-bd1d9de615e7@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c0b625af957bdd248f275b1c2112087b31d3e01e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 17 Feb 2025 15:38:54 +0000
+Subject: firmware: arm_ffa: Remove unnecessary declaration of
+ ffa_partitions_cleanup()
+
+From: Sudeep Holla <sudeep.holla@arm.com>
+
+[ 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 <viresh.kumar@linaro.org>
+Message-Id: <20250217-ffa_updates-v3-13-bd1d9de615e7@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 72aff53869b2ef053f359028876fc80de460665f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cc89da6aaae318eafe4a8df907197c63a2acde9b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0c9938f364c69defec5d911301afc13f710f437a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@arm.com>
+
+[ 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 <viresh.kumar@linaro.org>
+Message-Id: <20250217-ffa_updates-v3-8-bd1d9de615e7@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Stable-dep-of: 6d3daa9b8d31 ("firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7de52e482c89ce8e4f5ef7d4fe76da3401aaf4b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 32349dd4cb1d190154917545ca25443b303e2323 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <andy.shevchenko@gmail.com>
+
+[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ]
+
+Use the mem_is_zero() helper where possible.
+
+Signed-off-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/hte.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+-#include <linux/kernel.h>
+ #include <linux/kfifo.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+@@ -26,6 +25,7 @@
+ #include <linux/rbtree.h>
+ #include <linux/seq_file.h>
+ #include <linux/spinlock.h>
++#include <linux/string.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+@@ -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
+
--- /dev/null
+From 11150f2c88dc12bb2262ec49b0635dd04bf7bdb0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7625271ccb55aa33a49a28834f74920809ce7b9f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 10:33:16 +0200
+Subject: HID: uclogic: Fix regression of input name assignment
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e4b72fa4cc7eee84ec823e5750699207a02481b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9779bd7c83c83ecd298ddf0fa446ec2ca031a061 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:14 -0700
+Subject: ice: fix setting RSS VSI hash for E830
+
+From: Marcin Szycik <marcin.szycik@linux.intel.com>
+
+[ 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 <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a402b1074f66f824d5991449d1271f6b0d944fc6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:11 -0700
+Subject: ice: ptp: serialize E825 PHY timer start with PTP lock
+
+From: Grzegorz Nitka <grzegorz.nitka@intel.com>
+
+[ 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 <Arkadiusz.kubalewski@intel.com>
+Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Alexander Nowlin <alexander.nowlin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 54a5069e387744d72708eec5f90a746f2c69f877 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <axboe@kernel.dk>
+
+[ 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 <krisman@suse.de>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/file.h>
+ #include <linux/slab.h>
+ #include <linux/net.h>
++#include <linux/un.h>
+ #include <linux/compat.h>
+ #include <net/compat.h>
+ #include <linux/io_uring.h>
+@@ -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
+
--- /dev/null
+From 5dd9d37dbf8db894f0ce91372705fb4c8b93ba96 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 23:03:28 -0400
+Subject: ipv6: route: Unregister netdevice notifier on BPF init failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ 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 <dbgh9129@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bf8bf6a1a4cdfed43e48132ee6c23ad804b31a4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jiayuan.chen@linux.dev>
+
+[ 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 <bigeasy@linutronix.de>
+Suggested-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1c53bc3dbaac3b9ee8590c617a274d0b6316863c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d643a53a76bd0a8050585d5b9c2ba1f385e53875 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <viktor_jaegerskuepper@freenet.de>
+
+[ 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 <linux@weissschuh.net>
+Signed-off-by: Viktor Jägersküpper <viktor_jaegerskuepper@freenet.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Tested-by: Nathan Chancellor <nathan@kernel.org>
+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 <nsc@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3e98ee0d86595bcdcb48471e5f47f84a617f0e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <jianpeng.chang.cn@windriver.com>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3d259818008ca5308690017a0b4a72a5abf4ab4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From 9d84933e13953a3b02825af18ebec82d13acbd85 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a4bb3cc0217f23449bb43f6f41ff6a2d0bfff705 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:05:07 +0800
+Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions
+
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+
+[ 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 <yangtiezhu@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0f1bc6a8a80dcbeee8f5535bb9a74e26b1733def Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a8f21edc8e1f2c4d00628a10b16f22bfbd3d3f4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Oct 2025 16:45:37 +0200
+Subject: net: bridge: Flush multicast groups when snooping is disabled
+
+From: Petr Machata <petrm@nvidia.com>
+
+[ 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 <petrm@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From badbd1907cf261c39ce9f7ecf56607b8bf946150 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 89828792f845cb227c8457a95a695bc2d15e9d2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 99e323ff030eed1980df3e3bc030c1fee9d32491 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1c4b485ecf2b5238ce70e06eb382d659b0f7d365 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b730d410c266100c9f60e5be0071bde8a90e2ae2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f1059955fa740dde38c1b52d50368b2edefa2433 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8a324902655e7d17d43ce747fdc14831bf19f82c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 22:44:42 +0200
+Subject: net: gro: don't merge zcopy skbs
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ 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 <huzaifas@redhat.com>
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bf6333b1aa2c51e7b5039161da24168ec2e30adc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 21:43:11 +0900
+Subject: net: lan966x: avoid unregistering netdev on register failure
+
+From: Myeonghun Pak <mhun512@gmail.com>
+
+[ 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 <ae878000@gmail.com>
+Signed-off-by: Ijae Kim <ae878000@gmail.com>
+Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
+Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8eed84863bea83b6d45828b5b6179ee330991dff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From dbcb77a04256e6cfa721056df95ee6b65c77334f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 266155811a73cb5a770f5eb2b443cf11bbe959a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:33:02 +0300
+Subject: net/mlx5: Do not restore destination-less TC rules
+
+From: Jeroen Massar <jmassar@nvidia.com>
+
+[ 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 <jmassar@nvidia.com>
+Reviewed-by: Jianbo Liu <jianbol@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7fa9934c723ac1c91813d3085feb9b7f3a717a0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 09:19:47 +0200
+Subject: net: phy: DP83TC811: add reading of abilities
+
+From: Sven Schuchmann <schuchmann@schleissheimer.de>
+
+[ 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 <andrew@lunn.ch>
+Signed-off-by: Sven Schuchmann <schuchmann@schleissheimer.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de
+[pabeni@redhat.com: dropped revision history]
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a6adbecfdf6f8c1a77eff98740c86fe64870004e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
+Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bbdf7b82c539b5870fa0c6dd868622fc9705bfa9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4d2a55841e4c991bacaea1db7c612925d7cd8a72 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c8c33b5ed66d633645385b733572ce1ab6225d1f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0f0858de8fc62dd00bbf0a9a2edb6ed1b8f350fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4fecde4351a36da95d8d5db39d90cc979ea18278 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7957ef5e91f977c4d26639c0342b921e5d1ba667 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a824fb74dd93049c912c324a788fbb27121fb501 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jun 2025 17:44:23 +0200
+Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT.
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ 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 <fw@strlen.de>
+Co-developed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8d07f48651e8c171d698b0ce36915aaa96085d27 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Sep 2024 02:58:54 -0700
+Subject: netfilter: Make legacy configs user selectable
+
+From: Breno Leitao <leitao@debian.org>
+
+[ 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 <leitao@debian.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e0da8f8aafbbc0d4f4829171a14bb9607bce7aef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f47da49deed52dbdd3d10f928793fb2be6c4f9cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 56e2255297c4fab7ff8d74a2a60ff039f83ad9c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:20 +0200
+Subject: netfilter: x_tables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5852619bba7aaf111efc6a95ac81b2c86367343f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3e8a97aaba4e30162079ff2140d888d521251bda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:49 +0100
+Subject: netfs: Defer the emission of trace_netfs_folio()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dacd44a94f757de279ac9b107b5b6fa3bc176bf0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 5 Oct 2024 19:23:04 +0100
+Subject: netfs: Fix a few minor bugs in netfs_page_mkwrite()
+
+From: Matthew Wilcox (Oracle) <willy@infradead.org>
+
+[ 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) <willy@infradead.org>
+Link: https://lore.kernel.org/r/20241005182307.3190401-3-willy@infradead.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: ccde2ac757c7 ("netfs: Fix folio->private handling in netfs_perform_write()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3e649a3a2731585852ceb4c33e8e4059e5fd3176 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:54 +0100
+Subject: netfs: Fix early put of sink folio in netfs_read_gaps()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <sfrench@samba.org>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8cbd24bf657525c04658b68ced25adfccebd3954 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:58 +0100
+Subject: netfs: Fix folio->private handling in netfs_perform_write()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/pagevec.h>
+ #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
+
--- /dev/null
+From e5bfebc1db2d6d03ab72faaaad794cc126bb6e2a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <marc.dionne@auristor.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3c7a54e19c30d6535d24c81c71cf625eb7f9c377 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:47 +0100
+Subject: netfs: Fix overrun check in netfs_extract_user_iter()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 788c86a29be79a22bf5c252255d70af34f0389b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:57 +0100
+Subject: netfs: Fix partial invalidation of streaming-write folio
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1a138fd2cf6cf3e2172accee027b634554adfbc0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:51 +0100
+Subject: netfs: Fix potential deadlock in write-through mode
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fe04cc76b7f36dd1b2e6454fdb2b720e921137d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:50 +0100
+Subject: netfs: Fix streaming write being overwritten
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7b156b4fb32680bd8505e946baa6d8a0eadbf7ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <Slava.Dubeyko@ibm.com>
+
+[ 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] <TASK>
+[ 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] </TASK>
+[ 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 <Slava.Dubeyko@ibm.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+cc: Ceph Development <ceph-devel@vger.kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 43476490d29ddda39b158df3c712c1612d942a68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:53 +0100
+Subject: netfs: Fix write streaming disablement if fd open O_RDWR
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b03372bca2e983eed60f77975802bf511d28312b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 5 Oct 2024 19:23:05 +0100
+Subject: netfs: Remove unnecessary references to pages
+
+From: Matthew Wilcox (Oracle) <willy@infradead.org>
+
+[ Upstream commit e995e8b600260cff3cfaf2607a62be8bdc4aa9c7 ]
+
+These places should all use folios instead of pages.
+
+Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
+Link: https://lore.kernel.org/r/20241005182307.3190401-4-willy@infradead.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: ccde2ac757c7 ("netfs: Fix folio->private handling in netfs_perform_write()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 13bc52cc7e623e720521777b49ea4e116e32f41b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Apr 2026 14:52:59 -0400
+Subject: NFSD: Fix infinite loop in layout state revocation
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <dai.ngo@oracle.com>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Tested-by: Dai Ngo <dai.ngo@oracle.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5268fbaa3bbf22af7f356dfe2bffbe994291228d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 19:23:01 +0800
+Subject: nsfs: fix wrong error code returned for pidns ioctls
+
+From: Zhihao Cheng <chengzhihao1@huawei.com>
+
+[ 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 <chengzhihao1@huawei.com>
+Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com
+Reviewed-by: Yang Erkun <yangerkun@huawei.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 05d6234907ab5fae847bd2ede171982efb6f6261 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rkannoth@marvell.com>
+
+[ 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 <gakula@marvell.com>
+Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks")
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 136c2fa5b43f109f0923cc4ef1da233dc3c91616 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:58:42 +0000
+Subject: pds_core: ensure null-termination for firmware version strings
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ac8596435f22812be00ce4a43ca6eaf0e1404e0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:07 +0000
+Subject: pds_core: fix debugfs_lookup dentry leak and error handling
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3eed30b3b2e82abbdc4616fed647b5ff6711f59 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:05 +0000
+Subject: pds_core: fix error handling in pdsc_devcmd_wait
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a4e2cc2792fc77b9521569659eda12a190bf683c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 27e2463eff4e1fe6221736ca40b40204bcc025d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:44:58 +0530
+Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for
+ sm8150
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9157805f9eca468f5577bde0fed163dffd366779 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <biju.das.jz@bp.renesas.com>
+
+[ 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 <biju.das.jz@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5f7655a988b44e8130761f2588a4a418d3de1ef7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <oliverjwhite07@gmail.com>
+
+[ 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 <oliverjwhite07@gmail.com>
+Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8fe2c8784082acf6d1e1d0e4a41a3b927f954e67 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a53856014437af7328719e9c9a65fece5a1826a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 54922c12424aafe145d87650a95cef382e2cc7e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a4541eb2c0f96da38415aaa90188e74680028daf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 84dbc19e0c13e3b6da51cb25c57e0a53b3adc1d1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Apr 2026 17:15:45 +0100
+Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST
+
+From: Julian Braha <julianbraha@gmail.com>
+
+[ 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 <julianbraha@gmail.com>
+Tested-by: Gautam Menghani <gautam@linux.ibm.com>
+Reviewed-by: Amit Machhiwal <amachhiw@linux.ibm.com>
+Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f7efe3b3424de332d09a7485735d25b2d3ac08df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sayalip@linux.ibm.com>
+
+[ 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 <SF,ME,RI,LE> 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 <mahesh@linux.ibm.com>
+Acked-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
+[Maddy: Fixed the commit title]
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5801ce9d9bac9cdd7901946b8950b1c99ad710f6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 04467441ed85a2e305fd04fbadc9c0076fc3705d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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) <guoren@kernel.org>
+
+[ 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 <alexghiti@rivosinc.com>
+Cc: Björn Töpel <bjorn@rivosinc.com>
+Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
+Tested-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
+Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org
+[pjw@kernel.org: cleaned up commit message]
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4c827983d88e597d40fb2d5fa25dd7abf31622a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 12:53:17 -0500
+Subject: scsi: sd: Fix return code handling in sd_spinup_disk()
+
+From: Mike Christie <michael.christie@oracle.com>
+
+[ 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 <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u
+Signed-off-by: Mike Christie <michael.christie@oracle.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From b7a4ba67ed979e5a73ddbc838bed1fa0290a7764 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a93fdad90ba3581b725dcf880e5e38a9c051056b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2587ce39cb2c106a7f5176aaf6e90bee96024a0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 69d6e4180e79a37109bf13b53270649b40a3a4dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: test_kprobes: clear kprobes between test runs
+
+From: Martin Kaiser <martin@kaiser.cx>
+
+[ 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 <martin@kaiser.cx>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0f42f8496a371af0bf67ac2eaa784c6cf2190bb3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 08:58:25 -0400
+Subject: tls: Preserve sk_err across recvmsg() when data has been copied
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <kuba@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a7313d4cb6805ff8350ce808c220e4b7314a319 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 78e1695f6b9ac0e46f66ba76fda34eaab7bba50d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 22:48:43 +0800
+Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter
+ validation
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+[ 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 <tom.leiming@gmail.com>
+Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fa6f43032f1eb21180ff81b555c7a448f5aa30a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 14:17:37 +0800
+Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged
+
+From: Kang Yang <kang.yang@oss.qualcomm.com>
+
+[ 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 <kang.yang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 57c0d01dba23f788c8069157d5ca4865ebeb7544 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ea0d4de68317700c6f89c14118c177d0d9510a74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f4598860dc83b16679f8f2457aeb059701142e65 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.leach@collabora.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
+Reviewed-by: P Praneesh <praneesh.p@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8b6338cfd5e7cd81d2aad418d4ba4bb545ae0f6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:10:31 +0200
+Subject: wifi: mac80211: fix MLE defragmentation
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ 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 <miriam.rachel.korenblit@intel.com>
+Reviewed-by: Ilan Peer <ilan.peer@intel.com>
+Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6eef618dcaf4310ae4cb1fec531ccb81b1014528 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 12:24:17 +0200
+Subject: x86/xen: Fix xen_e820_swap_entry_with_ram()
+
+From: Juergen Gross <jgross@suse.com>
+
+[ 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 <jbeulich@suse.com>
+Reviewed-by: Jan Beulich <jbeulich@suse.com>
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Message-ID: <20260505102417.208138-1-jgross@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3540689a8fb4572599820e3b0332b0dd95f2d5e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 22:58:15 +0200
+Subject: zonefs: handle integer overflow in zonefs_fname_to_fno
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ 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 <adobriyan@gmail.com>
+Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed")
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a7c84dad10d6eb621b502e4cd8c1c756e941246 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 12:39:01 -0700
+Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap
+
+From: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+
+[ 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 <lukas.maar@tugraz.at>
+Fixes: ff13be830333 ("accel/qaic: Add datapath")
+Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+[jhugo: fix braces from checkpatch --strict]
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a43899e730e98266082fa20f7e7a193074cfa308 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:12:38 +0800
+Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a748f764fac8031927d61a90721c01be7416ef3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:01:39 +0800
+Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8bd903dfdc02c93f48b35868f55b043bc110e6ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dnaim@cachyos.org>
+
+[ 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 <dnaim@cachyos.org>
+Link: https://patch.msgid.link/20260516111532.111463-1-dnaim@cachyos.org
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3dc487f491bd2d1b2c1d4ba9eb799427cdf89f77 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 10:39:14 +0700
+Subject: ALSA: scarlett2: Add missing error check when initialise Autogain
+ Status
+
+From: Robertus Diawan Chris <robertusdchris@gmail.com>
+
+[ 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 <robertusdchris@gmail.com>
+Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fc8ed58fac2ec82d8eba90c022257df48dc6aacb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:32:49 +0800
+Subject: ALSA: seq: Serialize UMP output teardown with event_input
+
+From: Zhang Cen <rollkingzzc@gmail.com>
+
+[ 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 <tiwai@suse.de>
+
+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 <rollkingzzc@gmail.com>
+Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 671667a4d847a9da5fd46e60e4a74cd37c5d62cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:10 +0100
+Subject: ARM: dts: renesas: genmai: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a26c87834582be29994b73cf1c58e69ba6eef9d9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:11 +0100
+Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bc5b96682da3b53826d0ae91d91f9b231426fcf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2eaa80cc175b819925dbae9ae8d832161b969c3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <cassiogabrielcontato@gmail.com>
+
+[ 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 <cassiogabrielcontato@gmail.com>
+Link: https://patch.msgid.link/20260511-asoc-amd-acp-sdw-legacy-dai-name-null-v1-1-dc6151b6da8a@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5f709685e086e8436f5eeb1d5b5c46f122b134be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 21:08:52 +0200
+Subject: ASoC: codecs: fs210x: fix possible buffer overflow
+
+From: Alexander A. Klimov <grandmaster@al2klimov.de>
+
+[ 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 <grandmaster@al2klimov.de>
+Link: https://patch.msgid.link/20260513190852.196723-2-grandmaster@al2klimov.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 01ad3ae55fb442ac86eef7a3c2f38d95aca90bde Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:30:57 +0100
+Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove()
+
+From: Richard Fitzgerald <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 764695e61fdac16b6e00a386e792db3525731675 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 17:32:23 +0800
+Subject: ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC
+
+From: Mac Chiang <mac.chiang@intel.com>
+
+[ 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 <mac.chiang@intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Link: https://patch.msgid.link/20260508093224.1246282-2-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 896a43f74d7e75b9e0614a7b1074b9599fba3794 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 17:32:24 +0800
+Subject: ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC
+
+From: Mac Chiang <mac.chiang@intel.corp-partner.google.com>
+
+[ 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 <mac.chiang@intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Link: https://patch.msgid.link/20260508093224.1246282-3-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6fddb67521bc17c9e3b31388bf1cce769cab3a3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <robertusdchris@gmail.com>
+
+[ 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 <robertusdchris@gmail.com>
+Link: https://patch.msgid.link/20260519054024.274741-1-robertusdchris@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 87df7e658f74a29a799f0e6e7cc2efa532612dd7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:36:36 -0500
+Subject: ASoC: SOF: amd: Fix error code handling in psp_send_cmd()
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ 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 <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-sound/agGES8vWrLOrBu28@stanley.mountain/
+Fixes: f120cf33d232 ("ASoC: SOF: amd: Use AMD_NODE")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Link: https://patch.msgid.link/20260511153638.724810-1-mario.limonciello@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 96f21e9f8095c05aaae0559fe2ccfaa8ed5ecee3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <iam@sung-woo.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 <cshi008@fiu.edu>
+Acked-by: Weidong Zhu <weizhu@fiu.edu>
+Acked-by: Dave Tian <daveti@purdue.edu>
+Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+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 <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 557c8b0c76d143a7e6567409ada497c7c41bef0d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 22:51:51 +0100
+Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user()
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ce6146039a4c8c2980339186a3639eb0c1434092 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 15:22:30 -0600
+Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request
+
+From: Casey Chen <cachen@purestorage.com>
+
+[ 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
+<linux/blk-integrity.h>, 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 <cachen@purestorage.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a526a872f281ccdd246f4caeb317639e87db71d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 00:32:48 +0530
+Subject: Bluetooth: btintel_pcie: Fix incorrect MAC access programming
+
+From: Kiran K <kiran.k@intel.com>
+
+[ 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 <kiran.k@intel.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 40f28155e6e90f5a4a2096a3f311cd6242df32a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:24:02 +0800
+Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths
+
+From: Jiajia Liu <liujiajia@kylinos.cn>
+
+[ 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 <liujiajia@kylinos.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d29ccbe7ffe9f18ca6fb1ffc5297fa10456ba6b2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 May 2026 23:56:26 +0900
+Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx
+
+From: Xingwang Xiang <v3rdant.xiang@gmail.com>
+
+[ 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 <v3rdant.xiang@gmail.com>
+Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ceb9be3dd8c925a54eb47474ee671b144e726fa1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6ef44a8f8dafed7e390f724d5d7e66acf0f50fbe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 12:40:06 +0100
+Subject: btrfs: add macros to facilitate printing of keys
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 84882fb9a9c3e9aac1e7b357d60147e0419cb7a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 13:07:11 -0700
+Subject: btrfs: check for subvolume before deleting squota qgroup
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a6070c11ebae7080fa399d78f43ee304f13274a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Dec 2025 15:33:49 -0800
+Subject: btrfs: check squota parent usage on membership change
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ab4d125d72f697037bb2d15521c57cd21bcb658 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <josef@toxicpanda.com>
+
+[ 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 <johannes.thumshirn@wdc.com>
+Signed-off-by: Josef Bacik <josef@toxicpanda.com>
+Signed-off-by: Daniel Vacek <neelx@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+[ add note ]
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 68cba38c49e44cb3bff9344b09653b501ae1abfd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 19:53:46 -0700
+Subject: btrfs: fix squota accounting during enable generation
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 69c6500243680c54f80c673e36971960abce1adc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Dec 2025 15:35:02 -0800
+Subject: btrfs: relax squota parent qgroup deletion rule
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 96a2e89d4d2866cc740950c3c96e6c08e2f3f1d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Nov 2025 17:06:46 +0100
+Subject: btrfs: remaining BTRFS_PATH_AUTO_FREE conversions
+
+From: David Sterba <dsterba@suse.com>
+
+[ 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 <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dde3819e817ce87b5c5b2228da0aa54274197011 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <fdmanana@suse.com>
+
+[ 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] <TASK>
+ [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 <boris@bur.io>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 01214121637660aca570c4e4fb0ea2d3073dbf9e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Oct 2025 13:16:26 +0100
+Subject: btrfs: use the key format macros when printing keys
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From aba7f76b8abda9915c39d29359fff28acba86c64 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 11:30:54 +0800
+Subject: cgroup: rstat: relax NMI guard after switch to try_cmpxchg
+
+From: Cunlong Li <shenxiaogll@gmail.com>
+
+[ 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 <shenxiaogll@gmail.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 24e5db500b77ffe22dc4e59db7760e3049016342 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 15:08:49 +0800
+Subject: cgroup/rstat: validate cpu before css_rstat_cpu() access
+
+From: Qing Ming <a0yami@mailbox.org>
+
+[ 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 <a0yami@mailbox.org>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/cpumask.h>
+ #include <linux/sched/cputime.h>
+
+ #include <linux/bpf.h>
+@@ -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
+
--- /dev/null
+From e6744fc3ab7994cc7ffb25a8dee2a65063c82ebd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+cc: Herbert Xu <herbert@gondor.apana.org.au>
+cc: Simon Horman <horms@kernel.org>
+cc: Chuck Lever <chuck.lever@oracle.com>
+cc: linux-afs@lists.infradead.org
+Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
+Tested-by: Marc Dionne <marc.dionne@auristor.com>
+Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From df6725b77b43e5765ca553d214ab926e73a38531 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 15:22:09 +0800
+Subject: dma-mapping: move dma_map_resource() sanity check into debug code
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <robin.murphy@arm.com>
+Signed-off-by: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Link: https://lore.kernel.org/r/20260513072209.1486986-1-jianpeng.chang.cn@windriver.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d7ce4bec4ca571120f84ca62bc2ce85211a5f87f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Apr 2026 14:41:13 -0700
+Subject: Documentation: intel_pstate: Fix description of asymmetric packing
+ with SMT
+
+From: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
+
+[ 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 <ricardo.neri-calderon@linux.intel.com>
+[ 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 <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2ee3a0a836047beb7839fd8d3a49ff05d82cd2cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ankit.k.nautiyal@intel.com>
+
+[ 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 <mitulkumar.ajitkumar.golani@intel.com>
+Cc: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com
+(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fc2cecb8e9297b80206d81f98fb4bf61dab48ae3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 11:59:01 +0200
+Subject: drm/mediatek: mtk_cec: Fix non-static global variable
+
+From: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+
+[ 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 <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2f72b50551dc25001add3321a0b5c7d63d398956 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 11:59:02 +0200
+Subject: drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable
+
+From: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+
+[ 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 <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9cf5cacc77e7a317a7a85d88b978d8a649fd8fd1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 11 Apr 2026 17:59:15 +0300
+Subject: drm/msm/adreno: fix userspace-triggered crash on a2xx-a4xx
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Rob Clark <rob.clark@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/717778/
+Message-ID: <20260411-adreno-fix-ubwc-v3-1-4983156f3f80@oss.qualcomm.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f64fc839550496afb27a272c5460edaa9548df9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 03:24:58 +0300
+Subject: drm/msm/dpu: don't mix devm and drmm functions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <christophe.jaillet@wanadoo.fr>
+Closes: https://lore.kernel.org/r/78c764b8-44cf-4db5-88e7-807a85954518@wanadoo.fr
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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 <drm/drm_edid.h>
+ #include <drm/drm_framebuffer.h>
++#include <drm/drm_managed.h>
+
+ #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
+
--- /dev/null
+From 85b316bcab133ba20c36028ebd44068c55d898a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 20:21:38 +0300
+Subject: drm/msm/dsi: don't dump registers past the mapped region
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2a8d034e092ac134e7eb545775aff6a3eff2771b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 65f83c5574d092230011fa51a7c330f0ec048084 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From e4f6de776ba73ab31768b571d0508bbf0eaf71c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <daniele.ceraolospurio@intel.com>
+Assisted-by: Claude:claude-opus-4.6
+Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 38e0fa7bd2819c0ab67ffa26a50eeb6b1e7769bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <ashutosh.dixit@intel.com>
+Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3a56cc76042fa6db6fcb9443529447244883ec11 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 23:19:18 +0530
+Subject: drm/xe/pf: Fix CFI failure in debugfs access
+
+From: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+
+[ 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 <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com
+(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2ccca78a5881f118ba72ad91a7402d18c19bc6db Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 17:57:26 +0200
+Subject: drm/xe/vf: Fix signature of print functions
+
+From: Michal Wajdeczko <michal.wajdeczko@intel.com>
+
+[ 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 <michal.wajdeczko@intel.com>
+Cc: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Shuicheng Lin <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com
+(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bd334fb762e43eefc1455a145cddf8eeb0899eb7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 12:34:31 +0800
+Subject: erofs: fix managed cache race for unaligned extents
+
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+
+[ Upstream commit 649932fc3815eda2f24eb4de4b3a5e94886ee0b9 ]
+
+After unaligned compressed extents were introduced, the following race
+could occur:
+
+[Thread 1] [Thread 2]
+(z_erofs_fill_bio_vec)
+<handle a Z_EROFS_PREALLOCATED_FOLIO folio>
+...
+filemap_add_folio (1)
+ (z_erofs_bind_cache)
+ <the same folio is found..>
+ ..
+ ..
+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 <avkrasnov@salutedevices.com>
+Closes: https://lore.kernel.org/r/4a2f3801-fac1-42fe-ae75-da315822e088@salutedevices.com
+Tested-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 08c3a93c0f0b9a46e23ffdaab46dbd1f7561934b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2b42909b365d2f78fc50b75b0f4f03166452b4eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:33 +0100
+Subject: firmware: arm_ffa: Align RxTx buffer size before mapping
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sebastianene@google.com>
+Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com
+Reviewed-by: Sebastian Ene <sebastianene@google.com>
+Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org
+Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7da0d3c768709e3d8f028e7f49ea31c291ab681e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:30 +0100
+Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4ad27ba56c6ac552a1fe374d82a20a8dc8e642e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 34ba8db8ccd0d3f944e5645064bc37dbb2ebde05 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:28 +0100
+Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in
+ workqueue
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f17685ad1e730051980fb87d73281eed7c506c45 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:35 +0100
+Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 08c54466636a197ee96acb2b04c0ef6f3e7b3d18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:31 +0100
+Subject: firmware: arm_ffa: Keep framework RX release under lock
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0d8a0662b72be426465fa745c90ea7ff54c6d1e4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e9fc3a80e61e4c26231d23992a453528fc564ab8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:34 +0100
+Subject: firmware: arm_ffa: Snapshot notifier callbacks under lock
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4a2ccf93ffac96e52b409c92bc2219bfbab33668 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 85758ca4b71817b16da1c460308448127ed3d1e8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:32 +0100
+Subject: firmware: arm_ffa: Validate framework notification message layout
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2e0ecd7ed67670470206d3ccdd5f9309dc99dda0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 16:46:29 +0900
+Subject: fprobe: Fix unregister_fprobe() to wait for RCU grace period
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ 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) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b7f838922230c0c9ef34cb5c59fc20427646639f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 1 May 2026 15:10:58 +0800
+Subject: fs: Fix return in jfs_mkdir and orangefs_mkdir
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ 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 <zenghongling@kylinos.cn>
+Link: https://patch.msgid.link/20260501071058.1243245-1-zenghongling@kylinos.cn
+Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 41481414df559d4861151fa16a824dd5fe079ce9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <graypanda.inzag@gmail.com>
+
+[ 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 <graypanda.inzag@gmail.com>
+Link: https://patch.msgid.link/20260504112649.1862936-1-graypanda.inzag@gmail.com
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2ceaab0211f56e6a5686671b82d0fc6a18a1322b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 14 Mar 2026 14:24:56 +0100
+Subject: gcc-plugins: Always define CONST_CAST_GIMPLE and CONST_CAST_TREE
+
+From: Kees Cook <kees@kernel.org>
+
+[ 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 <ingo@hannover.ccc.de>
+Link: https://lore.kernel.org/lkml/ab6OKoay0OWkywjK@spatz.zoo
+Fixes: 6b90bd4ba40b ("GCC plugin infrastructure")
+Tested-by: Ivan Bulatovic <combuster@archlinux.us>
+Tested-by: Christopher Cradock <christopher@cradock.myzen.co.uk>
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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<gimple>((X))
++#undef CONST_CAST_TREE
++#define CONST_CAST_TREE(X) const_cast<tree>((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
+
--- /dev/null
+From 73044417702b6e0ec1e779a46400d2492e40a4ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 10:49:11 +0200
+Subject: gpio: aggregator: fix a potential use-after-free
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260520084911.27938-1-bartosz.golaszewski@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 14053a0543b5295084d287ca9c6ac588fcd9ca81 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 11:53:18 +0200
+Subject: gpio: aggregator: lock device when calling device_is_bound()
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6d63f6121631a838f01d3f9a7d491e135282fbc2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 14:16:31 +0200
+Subject: gpio: aggregator: remove the software node when deactivating the
+ aggregator
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <geert@linux-m68k.org>
+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 <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4709d039b3cf804bd159a2665989e011c370e79b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Mar 2026 11:31:12 +0100
+Subject: gpio: aggregator: stop using dev-sync-probe
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260327-gpio-kill-dev-sync-probe-v1-2-efac254f1a1d@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Stable-dep-of: 61fef83f239e ("gpio: aggregator: remove the software node when deactivating the aggregator")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/gpio/forwarder.h>
+ #include <linux/gpio/machine.h>
+
+-#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
+
--- /dev/null
+From 36b77146ae9b4532ad7536cd912965aa1848d466 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 806f84e2d069db44a1177da3118a0463caec68ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Apr 2026 10:10:02 +0300
+Subject: HID: intel-thc-hid: Intel-quickspi: Fix some error codes
+
+From: Dan Carpenter <error27@gmail.com>
+
+[ 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 <error27@gmail.com>
+Reviewed-by: Even Xu <even.xu@intel.com>
+Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 94fa6b4da69ba8bd7a04db506c3d56f7386f1048 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e393f17b8c38d3258f22a51f4456e87f844049ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 10:33:16 +0200
+Subject: HID: uclogic: Fix regression of input name assignment
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e34d0123a9c8a387e25d1bd67a280b4ccae7b902 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 14:41:00 -0700
+Subject: hwmon: (lm90) Add lock protection to lm90_alert
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6eeac13db192319546f7b13e2933508f928e1879 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 14:31:49 -0700
+Subject: hwmon: (lm90) Stop work before releasing hwmon device
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cb589338d73bcf379c095ade284e03b0e5987946 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 33989d1d6333bec3d337c0c1da6bb1cab53d16ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:14 -0700
+Subject: ice: fix setting RSS VSI hash for E830
+
+From: Marcin Szycik <marcin.szycik@linux.intel.com>
+
+[ 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 <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bd010d2d05035c9df5a5b15af3bf23a54e7d20b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:11 -0700
+Subject: ice: ptp: serialize E825 PHY timer start with PTP lock
+
+From: Grzegorz Nitka <grzegorz.nitka@intel.com>
+
+[ 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 <Arkadiusz.kubalewski@intel.com>
+Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Alexander Nowlin <alexander.nowlin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4587eb6d46e721e960a50a7dd076354391ca253c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:12 -0700
+Subject: ice: ptp: use primary NAC semaphore on E825
+
+From: Grzegorz Nitka <grzegorz.nitka@intel.com>
+
+[ 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 <Arkadiusz.kubalewski@intel.com>
+Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Alexander Nowlin <alexander.nowlin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-6-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6029aaee60034a2315247cb8adedd54682e465f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <emil.s.tantilov@intel.com>
+
+[ 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] <TASK>
+[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] </TASK>
+
+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 <emil.s.tantilov@intel.com>
+Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-3-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 15938d812debf6cc730f92936b66fac79f5a8c98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:15 -0700
+Subject: igc: set tx buffer type for SMD frames
+
+From: Kohei Enju <kohei@enjuk.jp>
+
+[ 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 <kohei@enjuk.jp>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Avigail Dahan <avigailx.dahan@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-9-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d784f7e924e36625a1d244c04be719afc8650d42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <axboe@kernel.dk>
+
+[ 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 <krisman@suse.de>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/file.h>
+ #include <linux/slab.h>
+ #include <linux/net.h>
++#include <linux/un.h>
+ #include <linux/compat.h>
+ #include <net/compat.h>
+ #include <linux/io_uring.h>
+@@ -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
+
--- /dev/null
+From 2deda1613f0cbde1acc13d8a0c78ff73eeed4e81 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:00:44 +0200
+Subject: io_uring/nop: pass all errors to userspace
+
+From: Alexander A. Klimov <grandmaster@al2klimov.de>
+
+[ 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 <grandmaster@al2klimov.de>
+Link: https://patch.msgid.link/20260520180045.538533-1-grandmaster@al2klimov.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f5f08571ba8399f8676c32a5ff537434609a0276 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jiayuan.chen@linux.dev>
+
+[ 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 <bigeasy@linutronix.de>
+Suggested-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e57933835906642b636d7ce7a3941523367fac5d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9dabc67ffe85e540e68173c0dadded520497098e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <viktor_jaegerskuepper@freenet.de>
+
+[ 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 <linux@weissschuh.net>
+Signed-off-by: Viktor Jägersküpper <viktor_jaegerskuepper@freenet.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Tested-by: Nathan Chancellor <nathan@kernel.org>
+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 <nsc@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 633d459b3749ce9ba8969924e1b660d18effe138 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <jianpeng.chang.cn@windriver.com>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f4b5f17f6a6982c069d49dcdc2280c666eb7ce67 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 23:27:19 +0900
+Subject: ksmbd: fix durable reconnect error path file lifetime
+
+From: Junyi Liu <moss80199@gmail.com>
+
+[ 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 <moss80199@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b58a2e3d68382af55d44cf6738d114f832faf21f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From 283d9e6b92dd85358445554c6ae89cd5d979ecaa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 32e8102c50faa7103ad21122b832d455e4113e2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:05:07 +0800
+Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions
+
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+
+[ 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 <yangtiezhu@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6e1f12ad42a85a879c734a6611f8149efdbe2fac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 85b7e40ee022366e02833f83a1fd76ec2548b67f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 15:12:02 +0200
+Subject: net: airoha: Disable GDM2 forwarding before configuring GDM2 loopback
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+[ 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 <madhur.agrawal@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From da9c6071b2ca092621c623055a57244eca90db1d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 15:44:57 +0200
+Subject: net: airoha: Fix NPU RX DMA descriptor bits
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ 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 <ansuelsmth@gmail.com>
+Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://patch.msgid.link/20260518134530.3683-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 45a60f2594e1c45bf0149276753cdc09e799dc97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Oct 2025 16:45:37 +0200
+Subject: net: bridge: Flush multicast groups when snooping is disabled
+
+From: Petr Machata <petrm@nvidia.com>
+
+[ 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 <petrm@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 11152070660df30b77b41f230fea9fe8c90054fe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d1c2fec0318b9039db87eca18f50262cefea9de7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5d9bac3ba442524fac4326166bb1faf9daeecd84 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a675397878270769154ead58ba39aed921ec74a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1b1752fe7b9b30d73e8f4e82a893768ee14970cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 14bd17a74135e488b178914d087471a693b16a28 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4e02d11c9d31ab4ba37ece0de8558983c4bf801f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 22:44:42 +0200
+Subject: net: gro: don't merge zcopy skbs
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ 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 <huzaifas@redhat.com>
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 811f65536185429e2a6ce71bcd1f97a24e818950 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 21:43:11 +0900
+Subject: net: lan966x: avoid unregistering netdev on register failure
+
+From: Myeonghun Pak <mhun512@gmail.com>
+
+[ 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 <ae878000@gmail.com>
+Signed-off-by: Ijae Kim <ae878000@gmail.com>
+Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
+Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 795e277d1bbc898aa93f39657d5b8e37daa94232 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From c020dbcff8f5be73937670ffba10a2c295965dd0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8474a9b55cfc4337c8f9b05f9e42efc3fa62726c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:33:02 +0300
+Subject: net/mlx5: Do not restore destination-less TC rules
+
+From: Jeroen Massar <jmassar@nvidia.com>
+
+[ 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 <jmassar@nvidia.com>
+Reviewed-by: Jianbo Liu <jianbol@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cd73eb5ce31f34c20602cd1960e99324861d4370 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 23:59:00 +0100
+Subject: net/mlx5e: Fix eswitch mode block underflow on IPsec acquire SA
+
+From: Prathamesh Deshpande <prathameshdeshpande7@gmail.com>
+
+[ 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 <prathameshdeshpande7@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260510225903.13184-1-prathameshdeshpande7@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d6b25ac792afdecf4f7937e99d5bb41e08a68ce9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 09:08:08 +0000
+Subject: net: napi: Avoid gro timer misfiring at end of busypoll
+
+From: Dragos Tatulea <dtatulea@nvidia.com>
+
+[ 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 <mkarsten@uwaterloo.ca>
+Signed-off-by: Martin Karsten <mkarsten@uwaterloo.ca>
+Signed-off-by: Dragos Tatulea <dtatulea@nvidia.com>
+Reviewed-by: Tariq Toukan <tariqt@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Reviewed-by: Joe Damato <joe@dama.to>
+Link: https://patch.msgid.link/20260506090808.820559-2-dtatulea@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bcacd72b8fbd62fd3a41c881f9ecedfbbc0824b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 09:19:47 +0200
+Subject: net: phy: DP83TC811: add reading of abilities
+
+From: Sven Schuchmann <schuchmann@schleissheimer.de>
+
+[ 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 <andrew@lunn.ch>
+Signed-off-by: Sven Schuchmann <schuchmann@schleissheimer.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de
+[pabeni@redhat.com: dropped revision history]
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eb4c8dcba9f27dc1696f8d7111dbe854b26afc38 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:23:10 +0200
+Subject: net: phy: honor eee_disabled_modes in phy_advertise_eee_all()
+
+From: Nicolai Buchwitz <nb@tipi-net.de>
+
+[ 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 <nb@tipi-net.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-2-05b52626fa68@tipi-net.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8690832ff1d697fcf1f56c7a1c03582022e2fac3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:23:09 +0200
+Subject: net: phy: honor eee_disabled_modes in phy_support_eee()
+
+From: Nicolai Buchwitz <nb@tipi-net.de>
+
+[ 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 <nb@tipi-net.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-1-05b52626fa68@tipi-net.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 816296d050dfed3db54f84d30c6afe2a1bdf6bcd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 15:13:24 -0700
+Subject: net: shaper: annotate the data races
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <horms@kernel.org>
+Link: https://patch.msgid.link/20260515221325.1685455-2-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: b8d7519352ba ("net: shaper: rework the VALID marking (again)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9897e9d2b14e0724c1fea5569ebe123f540aab7c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:03 -0700
+Subject: net: shaper: enforce singleton NETDEV scope with id 0
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From df95cf56e4e9b234ae48d1c6432d683208ca5daa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:56 -0700
+Subject: net: shaper: fix trivial ordering issue in net_shaper_commit()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5a5f918374135735091d3d31ed2e2753c01134f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:00 -0700
+Subject: net: shaper: fix undersized reply skb allocation in GROUP command
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 158dad15075bd0f59769497bcefab580521872f4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:55 -0700
+Subject: net: shaper: flip the polarity of the valid flag
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 17d4efbbdcd18719bc16c6d9f7c9e217c4bb68d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:57 -0700
+Subject: net: shaper: reject duplicate leaves in GROUP request
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-4-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 13ff86e23d003ac51ba826c1d8744f56699c6529 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:02 -0700
+Subject: net: shaper: reject handle IDs exceeding internal bit-width
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-9-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <uapi/linux/net_shaper.h>
+
++/* 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 <uapi/linux/net_shaper.h>
+
++#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
+
--- /dev/null
+From 30f8ca9610ee3066536723aa024f5f740be557cc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:04 -0700
+Subject: net: shaper: reject QUEUE scope handle with missing id
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e7869b15255a847e0aa5facbb5b33cf0021ed326 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 16:37:45 -0700
+Subject: net: shaper: Reject reparenting of existing nodes
+
+From: Mohsin Bashir <hmohsin@meta.com>
+
+[ 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 <hmohsin@meta.com>
+Link: https://patch.msgid.link/20260506233745.111895-1-mohsin.bashr@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1e48a0479cf17e4b93b59a5dfc8bb00d1dcc648a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 15:13:25 -0700
+Subject: net: shaper: rework the VALID marking (again)
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations")
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260515221325.1685455-3-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ec321bc1f6b39e542fb5b51cc40867971cf41174 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 22c09a5ef5c88555147f814f7d0962e1b6964cf1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
+Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a79dbedc99d654d267c6a28a8099f03cee6e0926 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9d670e661fe1dffdc2b96cbb62d122856bde69e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 01:28:13 +0530
+Subject: net: ti: icssm-prueth: fix eth_ports_node leak in probe
+
+From: Shitalkumar Gandhi <shital.gandhi45@gmail.com>
+
+[ 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 <shitalkumar.gandhi@cambiumnetworks.com>
+Link: https://patch.msgid.link/20260506195813.641610-1-shitalkumar.gandhi@cambiumnetworks.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e57d023882ee2244708064c8a1fc22066b06d73e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4ddd5298c405721d3927b2b8c47b760043ecff1a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ca7209a7695814e935e01be622d5bf7abf9ffc48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fcc0369d32409d652962874f47e23ddd1e247e41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ab47ec99dfb3b5107b85cd1680a39af7dc5621f0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4ce9385f60c68b623da626eed73567c97e1bbffb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 11:30:49 +0200
+Subject: netfilter: nft_inner: release local_lock before re-enabling softirqs
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 086e919f551bf17977a44a226a0a7597fb4b3051 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fffc92d43bb45e1418ec687474ca1613271a727d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5aa61e0dbd265765c5b4c6dc05aeedc905cc8d04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:20 +0200
+Subject: netfilter: x_tables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 78243b07cc33d454e3c47ee63f4752fdb7cc0dc1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f180c35d02a7eb2b39c341cb6c6efb4d4bd51734 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:34:00 +0100
+Subject: netfs, afs: Fix write skipping in dir/link writepages
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-24-dhowells@redhat.com
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1b67f05070ca7d492c9b0a1f22439720a2bb008b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:49 +0100
+Subject: netfs: Defer the emission of trace_netfs_folio()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 77bcf9ab362ac83e21f2cd973afb4802eb1e3b46 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:38 +0100
+Subject: netfs: Fix cancellation of a DIO and single read subrequests
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-2-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 55bd2db9c272ffbbbbbf66c6f07402c680db01df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:54 +0100
+Subject: netfs: Fix early put of sink folio in netfs_read_gaps()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <sfrench@samba.org>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2b63201cb71aba730a1a5b541d599efa8f408fa7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:58 +0100
+Subject: netfs: Fix folio->private handling in netfs_perform_write()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/pagevec.h>
+ #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
+
--- /dev/null
+From a27b4314e7771c498a7762186fc5505f3c2a9b3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:55 +0100
+Subject: netfs: Fix leak of request in netfs_write_begin() error handling
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-19-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1653482cf7a41091fe4d2204e137183725809d8f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <marc.dionne@auristor.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b3f1c53def7314a2e2c63c509ce60808a061b402 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:59 +0100
+Subject: netfs: Fix netfs_read_folio() to wait on writeback
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-23-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2a102f7a47a949d18b74545cfb2b077c1937b15c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:41 +0100
+Subject: netfs: Fix netfs_read_to_pagecache() to pause on subreq failure
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-5-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8cc863f8be9757cc819109068243b8f8577757ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:47 +0100
+Subject: netfs: Fix overrun check in netfs_extract_user_iter()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e64ead2ae68266dcd51228cf87d475f03f7449b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:57 +0100
+Subject: netfs: Fix partial invalidation of streaming-write folio
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From eefc7a7081a648defbbb752e4bdb3e250a869262 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:51 +0100
+Subject: netfs: Fix potential deadlock in write-through mode
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3947f09c16e91d36ea9b80a56fffe14b3671e07 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:56 +0100
+Subject: netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4742d1a1557c8c76d915b24501cdcc15d80e2ca9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:52 +0100
+Subject: netfs: Fix read-gaps to remove netfs_folio from filled folio
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-16-dhowells@redhat.com
+Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading")
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9af6262fae71fa8482e1f2eb62c83433ea9ef7ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:50 +0100
+Subject: netfs: Fix streaming write being overwritten
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9e83e3eab718f4d2e7e5df80fb3df8777980b28e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <Slava.Dubeyko@ibm.com>
+
+[ 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] <TASK>
+[ 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] </TASK>
+[ 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 <Slava.Dubeyko@ibm.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+cc: Ceph Development <ceph-devel@vger.kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dd1b43cec5f2bf65d7aecdd32853455857c31847 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:53 +0100
+Subject: netfs: Fix write streaming disablement if fd open O_RDWR
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 345f0cf335af6d0c1471d9f5f0e7461090a53a6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Apr 2026 14:52:59 -0400
+Subject: NFSD: Fix infinite loop in layout state revocation
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <dai.ngo@oracle.com>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Tested-by: Dai Ngo <dai.ngo@oracle.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8d380f1c25710eb0db4a029196cf8312a27a2e23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 19:23:01 +0800
+Subject: nsfs: fix wrong error code returned for pidns ioctls
+
+From: Zhihao Cheng <chengzhihao1@huawei.com>
+
+[ 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 <chengzhihao1@huawei.com>
+Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com
+Reviewed-by: Yang Erkun <yangerkun@huawei.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1a9ce3257d91be54f4fde5ae3b0017925cb0d48f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 06:16:02 -0700
+Subject: nvme: fix bio leak on mapping failure
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <sagi@grimberg.me>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bab9eed00eecb33fe59ad99703620f890e9c0434 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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) <acelan.kao@canonical.com>
+
+[ 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:
+ <TASK>
+ 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
+ </TASK>
+
+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) <acelan.kao@canonical.com>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3eb7ad2acdf89e8f6c0615cee3776ac63e951264 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rkannoth@marvell.com>
+
+[ 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 <gakula@marvell.com>
+Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks")
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1023f5d8ae8999ec1048357887707f30f9bfafeb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 15:26:10 +0200
+Subject: ovpn: disable BHs when updating device stats
+
+From: Ralf Lici <ralf@mandelbit.com>
+
+[ 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 <ralf@mandelbit.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/netdevice.h>
++
+ /* 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
+
--- /dev/null
+From 00d8cdbbfd6d9e5173ebe398f1211ecc1dfacbee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 17 Mar 2026 14:47:56 +0100
+Subject: ovpn: fix race between deleting interface and adding new peer
+
+From: Antonio Quartulli <antonio@openvpn.net>
+
+[ 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 <imv4bel@gmail.com>
+Closes: https://lore.kernel.org/netdev/aaVgJ16edTfQkYbx@v4bel/
+Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
+Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object")
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d959978e063a7f8726caa75ed31bf641582aca2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 11:55:21 +0100
+Subject: ovpn: respect peer refcount in CMD_NEW_PEER error path
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <sd@queasysnail.net>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: David Carlier <devnexen@gmail.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3cde6cbeece31fc07563a31f4cfc9296504744fe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 11:55:20 +0100
+Subject: ovpn: tcp - use cached peer pointer in ovpn_tcp_close()
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <sd@queasysnail.net>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: David Carlier <devnexen@gmail.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2108a08b2b7e32b4f5ce55b44d356a89d2d8f29b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:58:42 +0000
+Subject: pds_core: ensure null-termination for firmware version strings
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c00e52c151be87a3d827e226e7408d2f2e1d3e35 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:07 +0000
+Subject: pds_core: fix debugfs_lookup dentry leak and error handling
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 935bfa7cc35f617715435acede9bc1d257039a2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:05 +0000
+Subject: pds_core: fix error handling in pdsc_devcmd_wait
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a917c9d3938f91a50fbc1523cce9e70d9fe6fb2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9cd32920537d34cb2c9bfbbef9b3740fe5864afd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Apr 2026 11:44:13 +0000
+Subject: pinctrl: meson: amlogic-a4: fix deadlock issue
+
+From: Xianwei Zhao <xianwei.zhao@amlogic.com>
+
+[ 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 <xianwei.zhao@amlogic.com>
+Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e86d24f7aae8a85ea7d18bd6fa23e36a6048316c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Apr 2026 16:55:24 +0530
+Subject: pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e5baae712635b7cf51dddd5e679b653d0485478f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:44:58 +0530
+Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for
+ sm8150
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 07be86348c7b8b363fc1d17e43f463265391cdde Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Apr 2026 15:52:34 +0200
+Subject: pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function
+
+From: Til Kaiser <mail@tk154.de>
+
+[ 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 <mail@tk154.de>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6554348c091b331743f318db6671e681391ccb44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <biju.das.jz@bp.renesas.com>
+
+[ 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 <biju.das.jz@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1ebe476548596e8f2a4e365346dd4182854eef9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Apr 2026 19:24:51 +0100
+Subject: pinctrl: renesas: rzg2l: Fix SMT register cache handling
+
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+
+[ 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 <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260413182456.811543-2-prabhakar.mahadev-lad.rj@bp.renesas.com
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 45a1f679a55ef23cd2fcd7e3f627473d0b5dfc4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <oliverjwhite07@gmail.com>
+
+[ 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 <oliverjwhite07@gmail.com>
+Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5afec3ce0ea5d957a828b411603a18288a54a0c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7d50c77179e9306bdd41d13f9315ab5ff1c417fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From da2589763f9cabc03d57cebea4dc426ba9a6035d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f37db1e042d18cf4fe30dc9d38501314c778aa6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c35022cd4f8fb768a143902ecbf7be213d9a4ec1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 16 Nov 2025 19:55:44 +0530
+Subject: powerpc: 82xx: fix uninitialized pointers with free attribute
+
+From: Ally Heev <allyheev@gmail.com>
+
+[ 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 <dan.carpenter@linaro.org>
+Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/
+Signed-off-by: Ally Heev <allyheev@gmail.com>
+Fixes: 4aa5cc1e0012 ("powerpc-km82xx.c: replace of_node_put() with __free")
+Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
+Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20251116-aheev-uninitialized-free-attr-km82xx-v2-1-4307e2b5300d@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e4bb731e8b429749c13e1a17fe534c833beeada6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Apr 2026 17:15:45 +0100
+Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST
+
+From: Julian Braha <julianbraha@gmail.com>
+
+[ 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 <julianbraha@gmail.com>
+Tested-by: Gautam Menghani <gautam@linux.ibm.com>
+Reviewed-by: Amit Machhiwal <amachhiw@linux.ibm.com>
+Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 21ac7b3c34689051a92354b937cc87c96a4c441f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sayalip@linux.ibm.com>
+
+[ 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 <SF,ME,RI,LE> 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 <mahesh@linux.ibm.com>
+Acked-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
+[Maddy: Fixed the commit title]
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f9cb3eef6f85d89d2983a668506e6c8adbc7412e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shirazsaleem@microsoft.com>
+
+[ 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 <shirazsaleem@microsoft.com>
+Signed-off-by: Konstantin Taranov <kotaranov@microsoft.com>
+Link: https://patch.msgid.link/20260512094209.264955-1-kotaranov@linux.microsoft.com
+Reviewed-by: Long Li <longli@microsoft.com>
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f931e04d5571ee31fdacb25efcaaac3eedd7179f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d2f3d1b5978e9ce51c06617a1a83ebdee2bd81c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Apr 2026 09:11:39 +0000
+Subject: riscv: errata: Fix bitwise vs logical AND in MIPS errata patching
+
+From: Michael Neuling <mikey@neuling.org>
+
+[ 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 <mikey@neuling.org>
+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 <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 45955e7aa2ee984561070d2b1ce585c295246384 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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) <guoren@kernel.org>
+
+[ 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 <alexghiti@rivosinc.com>
+Cc: Björn Töpel <bjorn@rivosinc.com>
+Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
+Tested-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
+Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org
+[pjw@kernel.org: cleaned up commit message]
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 426be55e9338a46c1ceface0caf04cecd6e8c614 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 12:53:17 -0500
+Subject: scsi: sd: Fix return code handling in sd_spinup_disk()
+
+From: Mike Christie <michael.christie@oracle.com>
+
+[ 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 <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u
+Signed-off-by: Mike Christie <michael.christie@oracle.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 32b188f13184f1c2036d817e422a160b8c0bd627 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:39:28 +0300
+Subject: selftests: net: Fix checksums in xdp_native
+
+From: Nimrod Oren <noren@nvidia.com>
+
+[ 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 <cjubran@nvidia.com>
+Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
+Signed-off-by: Nimrod Oren <noren@nvidia.com>
+Link: https://patch.msgid.link/20260520153928.3371765-1-noren@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From cd48f2a524f2d4b832531688f4cea56116cbf685 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 18:19:40 +0800
+Subject: selftests: ublk: cap nthreads to kernel's actual nr_hw_queues
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+[ 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 <tom.leiming@gmail.com>
+Link: https://patch.msgid.link/20260513101941.1373998-1-tom.leiming@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From 67a8d4fcfaa6ec654274ae6c56b11cfcebb6e28c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e2383041f2064b676fd68c58b218aa5c62a3072f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 00:57:38 -0700
+Subject: tap: fix stack info leak in tap_ioctl() SIOCGIFHWADDR
+
+From: Weiming Shi <bestswngs@gmail.com>
+
+[ 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 <xmei5@asu.edu>
+Signed-off-by: Weiming Shi <bestswngs@gmail.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20260520075736.3415676-3-bestswngs@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 59ff0e104eeddf582c1f06290077c82124f9b4ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From df967edce7a517b69c084c1558048f7b3fb0ab45 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 08c693ce95054aea3da80ce3b27a29e13d0ac03a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <edumazet@google.com>
+
+[ 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 <clm@meta.com>
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260519084611.2485277-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 09dde48718c984003f628ef1af8511dc2df0f13d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: test_kprobes: clear kprobes between test runs
+
+From: Martin Kaiser <martin@kaiser.cx>
+
+[ 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 <martin@kaiser.cx>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3b31acc672fe7ffce1b08da296ba10eda3dafd88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 08:58:25 -0400
+Subject: tls: Preserve sk_err across recvmsg() when data has been copied
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <kuba@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 25c494e5770f5653579bcf75ac4debe52ef6ebc6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 79278407564a7a7b910401468a2b79630f91aef8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 22:48:43 +0800
+Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter
+ validation
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+[ 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 <tom.leiming@gmail.com>
+Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ff7c3e800a28ca46626c870ebb32ed8495af387e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 11:29:48 +0200
+Subject: vsock/virtio: fix zerocopy completion for multi-skb sends
+
+From: Stefano Garzarella <sgarzare@redhat.com>
+
+[ 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 <maherazz04@gmail.com>
+Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
+Acked-by: Michael S. Tsirkin <mst@redhat.com>
+Acked-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
+Link: https://patch.msgid.link/20260514092948.268720-1-sgarzare@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c736755540f322167f32046402bed12c7b10f437 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 14:17:37 +0800
+Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged
+
+From: Kang Yang <kang.yang@oss.qualcomm.com>
+
+[ 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 <kang.yang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 417cac44661f42049f4d89f43d1cb1d59f08a1d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ad07aca162adb7e0eaa873a12ee2fde602bf7df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1dc438f43879a2ec921cf7ef84f34f2b858c8e56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.leach@collabora.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
+Reviewed-by: P Praneesh <praneesh.p@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 068835f726aefbf3dc3acff6c65d6f0658d8ded1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <miriam.rachel.korenblit@intel.com>
+
+[ 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 <johannes.berg@intel.com>
+Link: https://patch.msgid.link/20260515151351.371f40fc6711.I6a82cfe9655564e9c5731af91c36493b26b1208e@changeid
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1cf3c8aca145e95c2e5b485cedf76a11afa10036 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 4 Apr 2026 22:41:44 -0700
+Subject: wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is
+ disabled
+
+From: Cole Leavitt <cole@unwrap.rs>
+
+[ 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 <miriam.rachel.korenblit@intel.com>
+Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
+Signed-off-by: Cole Leavitt <cole@unwrap.rs>
+Link: https://patch.msgid.link/20260405054145.1064152-3-cole@unwrap.rs
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d8122bf9e6a888ba3432e3000bfc4e3eecbd1469 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 12:29:08 +0200
+Subject: wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs
+
+From: Alexandru Hossu <hossu.alexandru@gmail.com>
+
+[ 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 <hossu.alexandru@gmail.com>
+Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8e847ddfb68139e9decf9779a963a94505234c47 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:10:31 +0200
+Subject: wifi: mac80211: fix MLE defragmentation
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ 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 <miriam.rachel.korenblit@intel.com>
+Reviewed-by: Ilan Peer <ilan.peer@intel.com>
+Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a823f8f1c5e8e4d4d5f8b0b8315cdadaa00c979e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:57:32 +0530
+Subject: wifi: wilc1000: fix dma_buffer leak on bus acquire failure
+
+From: Shitalkumar Gandhi <shital.gandhi45@gmail.com>
+
+[ 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 <shitalkumar.gandhi@cambiumnetworks.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260511042732.998311-1-shitalkumar.gandhi@cambiumnetworks.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 58bc96e02ee0797ee3c6092a3adf7d500abe68fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 16 Mar 2026 16:12:00 +0100
+Subject: x86/mce: Restore MCA polling interval halving
+
+From: Borislav Petkov (AMD) <bp@alien8.de>
+
+[ 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 <lirongqing@baidu.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Reviewed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+Tested-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+Link: https://lore.kernel.org/r/20260112082747.2842-1-lirongqing@baidu.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ad710c5c32984d38b795f28569186bb529a740a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 12:24:17 +0200
+Subject: x86/xen: Fix xen_e820_swap_entry_with_ram()
+
+From: Juergen Gross <jgross@suse.com>
+
+[ 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 <jbeulich@suse.com>
+Reviewed-by: Jan Beulich <jbeulich@suse.com>
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Message-ID: <20260505102417.208138-1-jgross@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 119ab2d36e04f95aa06691b3da115cdd53eaf800 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 22:58:15 +0200
+Subject: zonefs: handle integer overflow in zonefs_fname_to_fno
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ 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 <adobriyan@gmail.com>
+Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed")
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b347f48fa72972d3f9e72c5012b6159e3e742569 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 12:39:01 -0700
+Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap
+
+From: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+
+[ 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 <lukas.maar@tugraz.at>
+Fixes: ff13be830333 ("accel/qaic: Add datapath")
+Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+[jhugo: fix braces from checkpatch --strict]
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 205e99bda6b8d25705601523784bee2c604acb2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:01:39 +0800
+Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0a94426882271a415f88a084a8fa92738d4ebb02 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:32:49 +0800
+Subject: ALSA: seq: Serialize UMP output teardown with event_input
+
+From: Zhang Cen <rollkingzzc@gmail.com>
+
+[ 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 <tiwai@suse.de>
+
+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 <rollkingzzc@gmail.com>
+Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0b1af96c7c0e6da1468d713b09477d714e510510 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 27 Feb 2024 09:53:00 +0100
+Subject: ALSA: seq: ump: Use guard() for locking
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a297fb159304d21bf98c79b3e8956cafaaa19624 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a524e7f889c486b2e5d4a0888bd19fc0cb505e99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:30:57 +0100
+Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove()
+
+From: Richard Fitzgerald <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0da75a1960341715c044aed69b0cf0fea70ea3ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 May 2024 16:15:17 -0700
+Subject: Bluetooth: btmtk: add the function to get the fw name
+
+From: Sean Wang <sean.wang@mediatek.com>
+
+[ Upstream commit 00f993fdec06c8f036a1b9c8ee6b004c17143bd1 ]
+
+Include a shared function to get the firmware name, to prevent repeating
+code for similar chipsets.
+
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 516c05d1903a33ab6b5970d4977cc3c1d72afe0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:24:02 +0800
+Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths
+
+From: Jiajia Liu <liujiajia@kylinos.cn>
+
+[ 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 <liujiajia@kylinos.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ebf1e3642378fb0230660a9ac872af76096d9277 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Jul 2024 14:01:13 +0800
+Subject: Bluetooth: btmtk: move btusb_mtk_hci_wmt_sync to btmtk.c
+
+From: Chris Lu <chris.lu@mediatek.com>
+
+[ 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 <sean.wang@mediatek.com>
+Signed-off-by: Chris Lu <chris.lu@mediatek.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/module.h>
+ #include <linux/firmware.h>
++#include <linux/usb.h>
+
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+@@ -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 <sean.wang@mediatek.com>");
+ MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
+ 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 <linux/of.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/skbuff.h>
++#include <linux/usb.h>
+
+ #include <linux/mmc/host.h>
+ #include <linux/mmc/sdio_ids.h>
+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 <linux/regulator/consumer.h>
+ #include <linux/serdev.h>
+ #include <linux/skbuff.h>
++#include <linux/usb.h>
+
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+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
+
--- /dev/null
+From 148c4e3a4b2c9f3c7d429d890faaf9913c13e76e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Jul 2024 14:01:11 +0800
+Subject: Bluetooth: btmtk: rename btmediatek_data
+
+From: Chris Lu <chris.lu@mediatek.com>
+
+[ Upstream commit d3e6236053958a8f1c7c7a885d9cecdd383e4615 ]
+
+Rename btmediatek_data to have a consistent prefix throughout the driver.
+
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Chris Lu <chris.lu@mediatek.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From af1b7942b2f6ad16759e19bf99095a25206291ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 May 2024 16:15:19 -0700
+Subject: Bluetooth: btusb: mediatek: refactor the function btusb_mtk_reset
+
+From: Hao Qin <hao.qin@mediatek.com>
+
+[ 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 <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Hao Qin <hao.qin@mediatek.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: dd1dda6b8d6e ("Bluetooth: btmtk: fix urb->setup_packet leak in error paths")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8064980dcc9142351b5c9aa23734cd349c0a352f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 May 2026 23:56:26 +0900
+Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx
+
+From: Xingwang Xiang <v3rdant.xiang@gmail.com>
+
+[ 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 <v3rdant.xiang@gmail.com>
+Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 70f117e9f9be084efd4d33f78bb594688a924da2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e4c5ce2572be577b32e770a39590962d5d28b2a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <fdmanana@suse.com>
+
+[ 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] <TASK>
+ [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 <boris@bur.io>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ce381fbc7cf4d09fc5faba511324a544290c44f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 20:21:38 +0300
+Subject: drm/msm/dsi: don't dump registers past the mapped region
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1b41d87079fad5bc247d4a0f0119585e03c23866 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5656da4a04984de5795568cce1f76a890394b708 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 37641df58abd3266b5da7df0e9fd47269ec8b299 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9e2bb4c8e09d31dbdf970bdb76f579794dc4e3ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b2476dd0ee8670e2cf1c59b16fec55689cb93624 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cf33a17085ec9a5af440069f896b4264091f248c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9c8adb54eb942900115bdd40fc4576e114195136 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <andy.shevchenko@gmail.com>
+
+[ Upstream commit e106b1dd38e723ec2bb2bf57ea9b2aff464b9423 ]
+
+Use the mem_is_zero() helper where possible.
+
+Signed-off-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20241110201706.16614-1-andy.shevchenko@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/hte.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+-#include <linux/kernel.h>
+ #include <linux/kfifo.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+@@ -25,6 +24,7 @@
+ #include <linux/rbtree.h>
+ #include <linux/seq_file.h>
+ #include <linux/spinlock.h>
++#include <linux/string.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+@@ -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
+
--- /dev/null
+From b9e545df738d3cd064e374fa51230340d341111a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c5a8f43403e7d2d1cf74e72563c5948237d5e79e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 10:33:16 +0200
+Subject: HID: uclogic: Fix regression of input name assignment
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5393eb8a08fb8da31ae51878187989bdb85c0f62 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a1e38ab8eff054c3b59a90aec95bed1365cef369 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 23:03:28 -0400
+Subject: ipv6: route: Unregister netdevice notifier on BPF init failure
+
+From: Yuho Choi <dbgh9129@gmail.com>
+
+[ 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 <dbgh9129@gmail.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260520030329.1061183-1-dbgh9129@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ef81ddb202205d6af82f6de9f7ed6ea9214d8ba5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jiayuan.chen@linux.dev>
+
+[ 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 <bigeasy@linutronix.de>
+Suggested-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f7b25beca0f0523d75fb5d9f03cf577da87822a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3db2e5b555abafc1ae750c6e0389042ca453f4a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <jianpeng.chang.cn@windriver.com>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dc4ebeaf10e9fa75c150c268b22b767955bd5f7a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From e2f2a935ab368b6236a999d1d436eaef67f1dc62 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f20d8291ce98cfa8a7a3b0e449d84fdf5ef80680 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:05:07 +0800
+Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions
+
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+
+[ 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 <yangtiezhu@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a02ad5089d1017829c94f930899595d91a7d2f85 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6e9d5a749306867e684d3df57d5737408c5807a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Oct 2025 16:45:37 +0200
+Subject: net: bridge: Flush multicast groups when snooping is disabled
+
+From: Petr Machata <petrm@nvidia.com>
+
+[ 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 <petrm@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
+Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 04b7baddcd8d22139ed597c08f1a63f8d426185b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 884a3599d988f18af0fc5dfd1537154183b4cbc5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ded777f1581a284e996d1f102597d987668620c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <arinc.unal@arinc9.com>
+
+[ 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 <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 3ac85bcfd404 ("net: dsa: mt7530: preserve VLAN tags on trapped link-local frames")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0b4454486fdb78828ec1ea617095e8e045217880 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8f5ac27dc7284539846639402544e320cb545380 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 221b033be0f077993d7f62e71f9dfc424682b51a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8cd5cac8e4283df7559bcf017a050b5aa593d1ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dd2477efe20311e898f97735727e7e3aa48c3493 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 22:44:42 +0200
+Subject: net: gro: don't merge zcopy skbs
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ 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 <huzaifas@redhat.com>
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a707d8c79d58bad38b637c479f0e97afa0f1747c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 21:43:11 +0900
+Subject: net: lan966x: avoid unregistering netdev on register failure
+
+From: Myeonghun Pak <mhun512@gmail.com>
+
+[ 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 <ae878000@gmail.com>
+Signed-off-by: Ijae Kim <ae878000@gmail.com>
+Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
+Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 311e9a3ce25db44fb48d2b3b6f6aa41d237bf68d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 7568a9cc1adbc3a31dddbd054a134e69f570b504 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c76036ecd485e6a653ea91b62182459206627774 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:33:02 +0300
+Subject: net/mlx5: Do not restore destination-less TC rules
+
+From: Jeroen Massar <jmassar@nvidia.com>
+
+[ 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 <jmassar@nvidia.com>
+Reviewed-by: Jianbo Liu <jianbol@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 06d2325a00e3b7045edf684e7b86ae5653716949 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 Dec 2023 06:41:43 +0100
+Subject: net: phy: c45: add genphy_c45_pma_read_ext_abilities() function
+
+From: Oleksij Rempel <o.rempel@pengutronix.de>
+
+[ 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 <o.rempel@pengutronix.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Link: https://lore.kernel.org/r/20231212054144.87527-2-o.rempel@pengutronix.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: c78bdba7b966 ("net: phy: DP83TC811: add reading of abilities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 531bbd6e217e8db89be1cf59c833335eb26cbc24 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 09:19:47 +0200
+Subject: net: phy: DP83TC811: add reading of abilities
+
+From: Sven Schuchmann <schuchmann@schleissheimer.de>
+
+[ 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 <andrew@lunn.ch>
+Signed-off-by: Sven Schuchmann <schuchmann@schleissheimer.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de
+[pabeni@redhat.com: dropped revision history]
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cad966469d4db2017ee09377e111853048a16a13 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
+Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 96f4883f70442fa673586bda55cd4499fcf99f77 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 91ce5d6e9891cc2412715200830c4c058c86f3ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e0019cec602350baebce9c54e184961f1744d5a7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 209d7fc49e05dc6bc6fa94dffaa381a99e7f5e78 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Jan 2024 16:42:48 +0100
+Subject: netfilter: arptables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Reviewed-by: Phil Sutter <phil@nwl.cc>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9cd2a62d96f43ca483d588ac324dad0e31a152c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 Mar 2024 21:15:52 -0700
+Subject: netfilter: arptables: Select NETFILTER_FAMILY_ARP when building
+ arp_tables.c
+
+From: Kuniyuki Iwashima <kuniyu@amazon.com>
+
+[ 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:
+ <TASK>
+ __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
+ </TASK>
+
+Fixes: 4654467dc7e1 ("netfilter: arptables: allow xtables-nft only builds")
+Reported-by: syzkaller <syzkaller@googlegroups.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 66369db872908fe432e1d407449372db0834fd98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 812f90f09db00caf502621c1b511708e54f7326b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:12 +0100
+Subject: netfilter: ebtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1f8996773559b470b18aa8d370e630503e096a72 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8537307f1432dc229dd72bbba23abf7139c20bbf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2bf5b7722bf6803b23acd83a7421e9eba760e47a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Jun 2025 17:44:23 +0200
+Subject: netfilter: Exclude LEGACY TABLES on PREEMPT_RT.
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ 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 <fw@strlen.de>
+Co-developed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b8effb0ad3a9f6fff1b2b97e499104c7b3cbcd34 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Sep 2024 02:58:54 -0700
+Subject: netfilter: Make legacy configs user selectable
+
+From: Breno Leitao <leitao@debian.org>
+
+[ 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 <leitao@debian.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 76190268d1575a7cf36ebb0f39ebec01c83d384f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 47c134bacc6253723f9853513c1cf61bd6d0060a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 13a37f515950e43f29e085dea0ca998b8925b9d1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:20 +0200
+Subject: netfilter: x_tables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d0ee182026590ec13c3c149da649f4e7751e7ea4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 67aec5a1e1f22a9082cf7f661a3163bae38a147b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 24 Jan 2024 10:21:11 +0100
+Subject: netfilter: xtables: allow xtables-nft only builds
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 152783ad008bcb793a21b8fc2186ae17cd702d56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 6 Feb 2024 14:55:53 +0100
+Subject: netfilter: xtables: fix up kconfig dependencies
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <lkp@intel.com>
+Reported-by: Randy Dunlap <rdunlap@infradead.org>
+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 <rdunlap@infradead.org>
+Tested-by: Randy Dunlap <rdunlap@infradead.org> # build-tested
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d1b5449f69dc8ba1e1a2bb85f6da285d79e1531d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:47 +0100
+Subject: netfs: Fix overrun check in netfs_extract_user_iter()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c626ad6b767e3bff32870257fc74a06ea8668df9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <suhui@nfschina.com>
+
+[ Upstream commit a1e4c334cbc9a80578c3784f8a3e7076bb19578d ]
+
+check the value of 'ret' after call 'devlink_info_version_stored_put'.
+
+Signed-off-by: Su Hui <suhui@nfschina.com>
+Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
+Link: https://lore.kernel.org/r/20231019083351.1526484-1-suhui@nfschina.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 3d4432d34c19 ("pds_core: ensure null-termination for firmware version strings")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f0f5ec3bc83c2d908a3935649b760a5a40200125 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:58:42 +0000
+Subject: pds_core: ensure null-termination for firmware version strings
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a47e189be2955c8ff1d263a9d6440aeb6223ccfc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:07 +0000
+Subject: pds_core: fix debugfs_lookup dentry leak and error handling
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8e6ba9a37b7986a11320075f8a8bfd9be1552f6b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:05 +0000
+Subject: pds_core: fix error handling in pdsc_devcmd_wait
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 19ecd55f2a84d9f4f5d010003b10eca7e9d15217 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 23d7f0fb1ce538d488aceffc7b795f164a34e6c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:44:58 +0530
+Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for
+ sm8150
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a5171911b83d0d629527fd0753aba34a60bccf00 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e18d2354cdd8f77312b537888c2a0537e48b79bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 200fb102691778f586a91404820618a3c30a311b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 74c5d386b375196d90e6aec8fd6efa3f13813587 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 38ea4bc7750fd4605db7667e28f412bc242b5c5e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sayalip@linux.ibm.com>
+
+[ 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 <SF,ME,RI,LE> 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 <mahesh@linux.ibm.com>
+Acked-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
+[Maddy: Fixed the commit title]
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 41023097ad8a4a40cda3469760a0e514252e51b3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 Sep 2023 13:24:21 +0200
+Subject: ptrace: Convert ptrace_attach() to use lock guards
+
+From: Peter Zijlstra <peterz@infradead.org>
+
+[ 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) <peterz@infradead.org>
+Reviewed-by: Oleg Nesterov <oleg@redhat.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7afb89e4e76b43c3e3b4720318bdf478a775eb98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From c2e24dca6b228e8bf3fb6555271e036fa0346bfa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9c6396dee720b9703eccab6e847ba57aed1b1a93 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jani.nikula@intel.com>
+
+[ 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 <kees@kernel.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240814100035.3100852-1-jani.nikula@intel.com
+Signed-off-by: Jani Nikula <jani.nikula@intel.com>
+Stable-dep-of: 3e6ccd790ed6 ("gpio: cdev: check if uAPI v2 config attributes are correctly zeroed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 89f12bfcfb3724abce206e1c4e1773f25ad9bbad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3243b1886e5f9af137a089f1bb692f6489e41bc1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: test_kprobes: clear kprobes between test runs
+
+From: Martin Kaiser <martin@kaiser.cx>
+
+[ 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 <martin@kaiser.cx>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 88e4e0efa55e8cec4b3f698e34bb547885a4406c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 08:58:25 -0400
+Subject: tls: Preserve sk_err across recvmsg() when data has been copied
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <kuba@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 80dfc80956c6d15d2ed8fc6382c50fa2be0e3a0b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From aafdeaa81191ce0a1398a8e881b38d67face5f71 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9cc576b56e15381decbc26769ca46cf863ae04ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6f77c9f722ee4c14332a8583fa81865eb03d76ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.leach@collabora.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
+Reviewed-by: P Praneesh <praneesh.p@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d28a66511853b98280795fb1ed1bd13f030c570d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 12:24:17 +0200
+Subject: x86/xen: Fix xen_e820_swap_entry_with_ram()
+
+From: Juergen Gross <jgross@suse.com>
+
+[ 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 <jbeulich@suse.com>
+Reviewed-by: Jan Beulich <jbeulich@suse.com>
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Message-ID: <20260505102417.208138-1-jgross@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a58443624964b15129902a579c60856840061d0e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 22:58:15 +0200
+Subject: zonefs: handle integer overflow in zonefs_fname_to_fno
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ 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 <adobriyan@gmail.com>
+Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed")
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 032998a31b2ba43e8b29e3f58d0fa36798fba341 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Apr 2026 12:39:01 -0700
+Subject: accel/qaic: Add overflow check to remap_pfn_range during mmap
+
+From: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+
+[ 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 <lukas.maar@tugraz.at>
+Fixes: ff13be830333 ("accel/qaic: Add datapath")
+Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
+Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
+Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+[jhugo: fix braces from checkpatch --strict]
+Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4e7c6f769edb491381204291e8c43e7073a9866b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:34:01 +0100
+Subject: afs: Fix the locking used by afs_get_link()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-25-dhowells@redhat.com
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/pagemap.h>
++#include <linux/iov_iter.h>
++#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
+
--- /dev/null
+From 197f8de6d7bc191be00db28089f0fe206608e52b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 09:58:41 -0500
+Subject: ALSA: hda/ca0132: Disable auto-detect on manual output select
+
+From: Matt DeVillier <matt.devillier@gmail.com>
+
+[ 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 <matt.devillier@gmail.com>
+Link: https://lore.kernel.org/CAFTm+6AfeXKf=b2frG4xC5yC4jjM9TkD6c8+dOWWFw6BDjDESw@mail.gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 82f244632c96c9c03e08755c7f90a57b397c8b97 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:12:38 +0800
+Subject: ALSA: hda: cs35l41: Put ACPI device on missing physical node
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From aeb2522bbf37d518e065030d4d218be4be2c0c2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 16:01:39 +0800
+Subject: ALSA: hda: cs35l56: Put ACPI device after setting companion
+
+From: Shuhao Fu <sfual@cse.ust.hk>
+
+[ 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 <sfual@cse.ust.hk>
+Tested-by: Simon Trimmer <simont@opensource.cirrus.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d6e69744f9326500b6173feadd56d348a756f87f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dnaim@cachyos.org>
+
+[ 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 <dnaim@cachyos.org>
+Link: https://patch.msgid.link/20260516111532.111463-1-dnaim@cachyos.org
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ffd1dc0f9c7e2c7eb3dcfe6a04cd16e88a44b914 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 10:39:14 +0700
+Subject: ALSA: scarlett2: Add missing error check when initialise Autogain
+ Status
+
+From: Robertus Diawan Chris <robertusdchris@gmail.com>
+
+[ 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 <robertusdchris@gmail.com>
+Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 97bdccaf940dddd29bb7baf8d47124e766315b8a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:32:49 +0800
+Subject: ALSA: seq: Serialize UMP output teardown with event_input
+
+From: Zhang Cen <rollkingzzc@gmail.com>
+
+[ 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 <tiwai@suse.de>
+
+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 <rollkingzzc@gmail.com>
+Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f03d290c0e8d4f23415fadf7eadd96d39f5d9229 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:10 +0100
+Subject: ARM: dts: renesas: genmai: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5065a69944411d60d9b399a571172f6f9fd5f3a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Mar 2026 00:42:11 +0100
+Subject: ARM: dts: renesas: rskrza1: Drop superfluous cells
+
+From: Marek Vasut <marek.vasut+renesas@mailbox.org>
+
+[ 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 <marek.vasut+renesas@mailbox.org>
+Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes")
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 120d16aeda2770e36c17d681cb03995c40eb6bd8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 21:15:37 +0200
+Subject: ARM: integrator: Fix early initialization
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <linus.walleij@linaro.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1b222f2f0fec3dd7845af254136dac8b93210b30 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <geert+renesas@glider.be>
+
+[ 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 <geert+renesas@glider.be>
+Link: https://patch.msgid.link/459d360a8332f92b3766b30814e7e1c76169aaf7.1767719254.git.geert+renesas@glider.be
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 = <GIC_ESPI 10 IRQ_TYPE_LEVEL_HIGH>;
+- 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 = <GIC_ESPI 11 IRQ_TYPE_LEVEL_HIGH>;
+- 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 = <GIC_ESPI 12 IRQ_TYPE_LEVEL_HIGH>;
+- 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 = <GIC_ESPI 13 IRQ_TYPE_LEVEL_HIGH>;
+- 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
+
--- /dev/null
+From f8f4cf0f70d2633df06c25402599d4f9fbfbd360 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <cassiogabrielcontato@gmail.com>
+
+[ 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 <cassiogabrielcontato@gmail.com>
+Link: https://patch.msgid.link/20260511-asoc-amd-acp-sdw-legacy-dai-name-null-v1-1-dc6151b6da8a@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d6860a37083f954a5f09927daea4ed83c57d49a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 21:08:52 +0200
+Subject: ASoC: codecs: fs210x: fix possible buffer overflow
+
+From: Alexander A. Klimov <grandmaster@al2klimov.de>
+
+[ 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 <grandmaster@al2klimov.de>
+Link: https://patch.msgid.link/20260513190852.196723-2-grandmaster@al2klimov.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 598df9b6dba53cbce187211cbf1bde6094ba52c4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:25:10 +0100
+Subject: ASoC: cs-amp-lib: Fix missing dput() after debugfs_lookup()
+
+From: Richard Fitzgerald <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 09b3ab004efd1b9426cdd8dad6d42ecff21102d3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 67c10bac25f140a99b2646fc0b85455bde6c5486 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:30:57 +0100
+Subject: ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove()
+
+From: Richard Fitzgerald <rf@opensource.cirrus.com>
+
+[ 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 <rf@opensource.cirrus.com>
+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 <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ff09a6224cdbf18e020f52d2d0316070321e640a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Apr 2026 09:23:35 +0100
+Subject: ASoC: intel: sof_sdw: Prepare for configuration without a jack
+
+From: Maciej Strozek <mstrozek@opensource.cirrus.com>
+
+[ 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 <mstrozek@opensource.cirrus.com>
+Link: https://patch.msgid.link/20260403082335.40798-1-mstrozek@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 5a30862dec5a ("ASoC: sdw_utils: Check speaker component string allocation")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4bc99beea2d7ee6adfedfa924c780529183edb55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 17:32:23 +0800
+Subject: ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC
+
+From: Mac Chiang <mac.chiang@intel.com>
+
+[ 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 <mac.chiang@intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Link: https://patch.msgid.link/20260508093224.1246282-2-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4917f62a474d9a4ead6e8bdb3617fdfa581775e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 17:32:24 +0800
+Subject: ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC
+
+From: Mac Chiang <mac.chiang@intel.corp-partner.google.com>
+
+[ 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 <mac.chiang@intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Link: https://patch.msgid.link/20260508093224.1246282-3-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3085b572d1d4d95f8d04d61b2548f0fb201263c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <cassiogabrielcontato@gmail.com>
+
+[ 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 <cassiogabrielcontato@gmail.com>
+Link: https://patch.msgid.link/20260512-asoc-sdw-utils-spk-components-alloc-v1-1-c9bbd6d2e123@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3c02e313be01f70a0ca13ef83c8b490b9ec114e0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Apr 2026 12:48:17 +0100
+Subject: ASoC: sdw_utils: cs42l43: allow spk component names to be combined
+
+From: Maciej Strozek <mstrozek@opensource.cirrus.com>
+
+[ 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 <yung-chuan.liao@linux.intel.com>
+Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
+Suggested-by: Aaron Ma <aaron.ma@canonical.com>
+Tested-by: Aaron Ma <aaron.ma@canonical.com>
+Link: https://patch.msgid.link/20260420114823.194226-1-mstrozek@opensource.cirrus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 5a30862dec5a ("ASoC: sdw_utils: Check speaker component string allocation")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 907de6b654c2321de5ed0042b842a98250b5bdda Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <robertusdchris@gmail.com>
+
+[ 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 <robertusdchris@gmail.com>
+Link: https://patch.msgid.link/20260519054024.274741-1-robertusdchris@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 94ea25ad954cf93e0ccd28b941c4d9068db7881e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:36:36 -0500
+Subject: ASoC: SOF: amd: Fix error code handling in psp_send_cmd()
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ 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 <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-sound/agGES8vWrLOrBu28@stanley.mountain/
+Fixes: f120cf33d232 ("ASoC: SOF: amd: Use AMD_NODE")
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Link: https://patch.msgid.link/20260511153638.724810-1-mario.limonciello@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c7bd6219f7abebfee6c57351ed6aa238a8cb229b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 12:02:53 -0700
+Subject: blk-mq: pop cached request if it is usable
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <kbusch@kernel.org>
+Link: https://patch.msgid.link/20260521190253.242065-1-kbusch@meta.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 14f2647cf484a268ad282678449f2d5152271a81 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Feb 2026 22:19:49 +0900
+Subject: block: allow submitting all zone writes from a single context
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ 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 <dlemoal@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 836efd35c472 ("block: fix handling of dead zone write plugs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/spinlock.h>
+ #include <linux/refcount.h>
+ #include <linux/mempool.h>
++#include <linux/kthread.h>
++#include <linux/freezer.h>
+
+ #include <trace/events/block.h>
+
+@@ -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 <linux/minmax.h>
+ #include <linux/timer.h>
+ #include <linux/workqueue.h>
++#include <linux/completion.h>
+ #include <linux/wait.h>
+ #include <linux/bio.h>
+ #include <linux/gfp.h>
+@@ -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
+
--- /dev/null
+From bcd0522e6a6a7ddf761021bf7a795f5f19e392aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 20:56:22 +0900
+Subject: block: avoid use-after-free in disk_free_zone_resources()
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ 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 <dlemoal@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260522115622.588535-1-dlemoal@kernel.org
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ab3a34ddfd365fcc98d114478f40ead3b2190460 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <iam@sung-woo.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 <cshi008@fiu.edu>
+Acked-by: Weidong Zhu <weizhu@fiu.edu>
+Acked-by: Dave Tian <daveti@purdue.edu>
+Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+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 <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 78fa36da7ce3c22d53d97634acf9a34ea01292fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 22:51:51 +0100
+Subject: block: don't overwrite bip_vcnt in bio_integrity_copy_user()
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 25f916dac4195fac69edba27f6e352bc414a9197 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 20:11:29 +0900
+Subject: block: fix handling of dead zone write plugs
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ 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 <shinichiro.kawasaki@wdc.com>
+Fixes: b7d4ffb51037 ("block: fix zone write plug removal")
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Link: https://patch.msgid.link/20260513111129.108809-1-dlemoal@kernel.org
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bb9b4aa6f52a1c4e5bd4463e4374bfa3a58c8323 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 15:22:30 -0600
+Subject: block: recompute nr_integrity_segments in blk_insert_cloned_request
+
+From: Casey Chen <cachen@purestorage.com>
+
+[ 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
+<linux/blk-integrity.h>, 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 <cachen@purestorage.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f5226eac4619db88a81295a657a72387808f10b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Feb 2026 22:19:48 +0900
+Subject: block: rename struct gendisk zone_wplugs_lock field
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+[ 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 <dlemoal@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Stable-dep-of: 836efd35c472 ("block: fix handling of dead zone write plugs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b135de4c20354f19cc4ab50af4e3fdb40852c3c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 00:32:48 +0530
+Subject: Bluetooth: btintel_pcie: Fix incorrect MAC access programming
+
+From: Kiran K <kiran.k@intel.com>
+
+[ 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 <kiran.k@intel.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 97f7f8af0d073186fac1815b748f3b0c4bca63e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:24:02 +0800
+Subject: Bluetooth: btmtk: fix urb->setup_packet leak in error paths
+
+From: Jiajia Liu <liujiajia@kylinos.cn>
+
+[ 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 <liujiajia@kylinos.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b6dce8c7e4351353582a8a037581165d4cba24b8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <luiz.von.dentz@intel.com>
+
+[ 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 <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5fa2822d11c4b4d5f059ca6b89d068eacccaab1e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 May 2026 23:56:26 +0900
+Subject: bpf, skmsg: fix verdict sk_data_ready racing with ktls rx
+
+From: Xingwang Xiang <v3rdant.xiang@gmail.com>
+
+[ 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 <v3rdant.xiang@gmail.com>
+Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7f8e47b2fcce55a7d891b9b2cb149dcf8b8928f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <idosch@nvidia.com>
+
+[ 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:
+<IRQ>
+__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))
+</IRQ>
+
+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 <tglx@kernel.org>
+Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4bc372a66435b33fc25a85c7ada2fb8411bcf75e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 13:07:11 -0700
+Subject: btrfs: check for subvolume before deleting squota qgroup
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 565bdb43305568d3192a951ad7e1b0c0c295c3c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 19:53:46 -0700
+Subject: btrfs: fix squota accounting during enable generation
+
+From: Boris Burkov <boris@bur.io>
+
+[ 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 <wqu@suse.com>
+Signed-off-by: Boris Burkov <boris@bur.io>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2dbab4a832da159be6065b08d96104b5e753691f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <fdmanana@suse.com>
+
+[ 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] <TASK>
+ [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 <boris@bur.io>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2a203624325f9f7bc9bc7d1981e57e45e4372820 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 18:34:06 +0800
+Subject: cachefiles: Fix error return when vfs_mkdir() fails
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ 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 <zenghongling@kylinos.cn>
+Link: https://patch.msgid.link/20260513103406.202320-1-zenghongling@kylinos.cn
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From caa94f6e9e5b1fbfe2a00e8f78348f47013745be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 11:30:54 +0800
+Subject: cgroup: rstat: relax NMI guard after switch to try_cmpxchg
+
+From: Cunlong Li <shenxiaogll@gmail.com>
+
+[ 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 <shenxiaogll@gmail.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 21dfa28d88cc7ed926678c7fb0a1f1948329c25e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 15:08:49 +0800
+Subject: cgroup/rstat: validate cpu before css_rstat_cpu() access
+
+From: Qing Ming <a0yami@mailbox.org>
+
+[ 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 <a0yami@mailbox.org>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/cpumask.h>
+ #include <linux/sched/cputime.h>
+
+ #include <linux/bpf.h>
+@@ -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
+
--- /dev/null
+From 4566d27fd90d24602fd7c8e60c806325603bcfb5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 22:26:22 +0900
+Subject: cifs: client: stage smb3_reconfigure() updates and restore ctx on
+ failure
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+[ 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 <rajasimandalos@gmail.com>
+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 <henrique.carvalho@suse.com>
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8afb5ac1689a2ebca77135c777f2777edf869321 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 22:13:09 +0100
+Subject: cifs: Fix undefined variables
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <lkp@intel.com>
+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 <sfrench@samba.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: Christian Brauner <brauner@kernel.org>
+cc: linux-cifs@vger.kernel.org
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c682aecfd60bf77a711d50018de602e91059253a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+cc: Herbert Xu <herbert@gondor.apana.org.au>
+cc: Simon Horman <horms@kernel.org>
+cc: Chuck Lever <chuck.lever@oracle.com>
+cc: linux-afs@lists.infradead.org
+Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
+Tested-by: Marc Dionne <marc.dionne@auristor.com>
+Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 14e01e8c281cbcd6be8263b91de8c39ce1f2c979 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 15:22:09 +0800
+Subject: dma-mapping: move dma_map_resource() sanity check into debug code
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <robin.murphy@arm.com>
+Signed-off-by: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Link: https://lore.kernel.org/r/20260513072209.1486986-1-jianpeng.chang.cn@windriver.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4c5887d0bf3976b6b973985a22418de4cc622096 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Apr 2026 14:41:13 -0700
+Subject: Documentation: intel_pstate: Fix description of asymmetric packing
+ with SMT
+
+From: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
+
+[ 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 <ricardo.neri-calderon@linux.intel.com>
+[ 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 <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cff53153f5a6ac5f6e22a0fde5cbc8c55c86dcb8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <wse@tuxedocomputers.com>
+
+[ 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 <W_Armin@gmx.de>
+Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Werner Sembach <wse@tuxedocomputers.com>
+Link: https://patch.msgid.link/20260324203413.454361-6-wse@tuxedocomputers.com
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Stable-dep-of: 26cbe119f99c ("platform/x86: uniwill-laptop: Do not enable the charging limit even when forced")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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 <wse@tuxedocomputers.com>
++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 <wse@tuxedocomputers.com>
++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
+
--- /dev/null
+From 489a88a40be0233795d6be6472d26951b2bd7176 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <timur.kristof@gmail.com>
+
+[ 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 <timur.kristof@gmail.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 530411b465ef0b2c0cc18c2e3d7e38422b1117d1)
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d9b8390a2930b5cbf0c6b031e527247417868e8f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <timur.kristof@gmail.com>
+
+[ 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 <timur.kristof@gmail.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit e729ae5f3ac73c861c062080ac8c3d666c972404)
+Stable-dep-of: 3e5a1d5bb2ff ("drm/amdgpu/vce1: Fix VCE 1 firmware size and offsets")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From abd256366b33524e467917e12d9cd6b82f814057 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <timur.kristof@gmail.com>
+
+[ 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 <timur.kristof@gmail.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit c16fe59f622a080fc457a57b3e8f14c780699449)
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 87e145dfa9fb5652507d7741afb05e0b36296e74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 13:41:45 +0200
+Subject: drm/gem: Make the GEM LRU lock part of drm_device
+
+From: Boris Brezillon <boris.brezillon@collabora.com>
+
+[ 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 <olvaffe@gmail.com>
+Closes: https://gitlab.freedesktop.org/panfrost/linux/-/work_items/86
+Reviewed-by: Rob Clark <rob.clark@oss.qualcomm.com>
+Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
+Reviewed-by: Steven Price <steven.price@arm.com>
+Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
+Link: https://patch.msgid.link/20260518-panthor-shrinker-fixes-v4-1-1920234470d5@collabora.com
+Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 65a6cf4b7a029d0ff25da756f0c35909c4ea0871 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ankit.k.nautiyal@intel.com>
+
+[ 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 <mitulkumar.ajitkumar.golani@intel.com>
+Cc: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com
+(cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6f18b326188f5893b2b18118e1874dff06413fe7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 11:59:01 +0200
+Subject: drm/mediatek: mtk_cec: Fix non-static global variable
+
+From: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+
+[ 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 <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 53f8b7dff1f2d1dc67a0b710637a7f78064196fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 11:59:02 +0200
+Subject: drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable
+
+From: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+
+[ 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 <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 906ee70f30df771d76ec01fdf3e929425a3c8a53 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <louisalexis.eyraud@collabora.com>
+
+[ 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 <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/
+Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3d03ba036d640f30ae3cd1ad4f7899f8696c3c06 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 11:59:00 +0200
+Subject: drm/mediatek: mtk_hdmi_v2: Fix non-static global variable
+
+From: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+
+[ 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 <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202604132044.fcYjEcU8-lkp@intel.com/
+Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+Reviewed-by: CK Hu <ck.hu@mediatek.com>
+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 <chunkuang.hu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2e70499bfcdf013c8a4b753c09b90ac4c4b14269 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Mar 2026 05:44:01 +0530
+Subject: drm/msm/a6xx: Add soft fuse detection support
+
+From: Akhil P Oommen <akhilpo@oss.qualcomm.com>
+
+[ 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 <akhilpo@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/714676/
+Message-ID: <20260327-a8xx-gpu-batch2-v2-12-2b53c38d2101@oss.qualcomm.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Stable-dep-of: e64bca63647d ("drm/msm/adreno: Fix a reference leak in a6xx_gpu_init()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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.
+ <bitfield pos="1" name="LPAC" type="boolean"/>
+ <bitfield pos="2" name="RAYTRACING" type="boolean"/>
+ </reg32>
++ <reg32 offset="0x0405" name="CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS" variants="A8XX-">
++ <bitfield high="8" low="0" name="FINALFREQLIMIT"/>
++ <bitfield pos="24" name="SOFTSKUDISABLED" type="boolean"/>
++ </reg32>
+ </domain>
+
+ </database>
+--
+2.53.0
+
--- /dev/null
+From 963bfa2bff5f7a40406eb9f7e5e103c4fb76b5f9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <nichen@iscas.ac.cn>
+
+[ 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 <nichen@iscas.ac.cn>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/721342/
+Message-ID: <20260428073558.1234238-1-nichen@iscas.ac.cn>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a8b4559b0388fc8de1d9943138740517e2182db6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 11 Apr 2026 08:03:12 -0700
+Subject: drm/msm/a6xx: Restore sysprof_active
+
+From: Rob Clark <robin.clark@oss.qualcomm.com>
+
+[ 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 <robin.clark@oss.qualcomm.com>
+Reviewed-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/717780/
+Message-ID: <20260411150312.257937-1-robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3668863400987b432eed6469d699b4601cf23f5c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 24 Jan 2026 00:37:38 +0800
+Subject: drm/msm/adreno: Fix a reference leak in a6xx_gpu_init()
+
+From: Felix Gu <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Patchwork: https://patchwork.freedesktop.org/patch/700661/
+Message-ID: <20260124-a6xx_gpu-v1-1-fa0c8b2dcfb1@gmail.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4d5d7916403eaf7ffe1ae790434c739facb9d0a6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 11 Apr 2026 17:59:15 +0300
+Subject: drm/msm/adreno: fix userspace-triggered crash on a2xx-a4xx
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Rob Clark <rob.clark@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/717778/
+Message-ID: <20260411-adreno-fix-ubwc-v3-1-4983156f3f80@oss.qualcomm.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5b5e3a09354d47cbb460cf1b511352dac0d0223c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 03:24:58 +0300
+Subject: drm/msm/dpu: don't mix devm and drmm functions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <christophe.jaillet@wanadoo.fr>
+Closes: https://lore.kernel.org/r/78c764b8-44cf-4db5-88e7-807a85954518@wanadoo.fr
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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 <drm/drm_edid.h>
+ #include <drm/drm_framebuffer.h>
++#include <drm/drm_managed.h>
+
+ #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
+
--- /dev/null
+From 5397342f97df39c1fedff93365ba6e0c231f9ca9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:14:25 +0530
+Subject: drm/msm/dpu: Fix Kaanapali CWB register configuration
+
+From: Mahadevan P <mahadevan.p@oss.qualcomm.com>
+
+[ 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 <mahadevan.p@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <dmitry.baryshkov@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d0860ae6ba5d858af3f44392e3b0f7be54aa49ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Apr 2026 17:14:30 +0200
+Subject: drm/msm/dpu: fix UV scanlines calculation for YUV UBWC formats
+
+From: Neil Armstrong <neil.armstrong@linaro.org>
+
+[ 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 <neil.armstrong@linaro.org>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <dmitry.baryshkov@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1bfdaa10b1581b2eef0a7334e507861adfa0d1c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 20:21:38 +0300
+Subject: drm/msm/dsi: don't dump registers past the mapped region
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9cc33bcb304438e1d05ede668132e671736d1be8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 14 Mar 2026 04:14:50 +0000
+Subject: drm/msm: Fix GMEM_BASE for A650
+
+From: Alexander Koskovich <akoskovich@pm.me>
+
+[ 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 <akoskovich@pm.me>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
+Patchwork: https://patchwork.freedesktop.org/patch/711880/
+Message-ID: <20260314-fix-gmem-base-a650-v1-1-3308f60cf74c@pm.me>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7ea136bb54ed25892475a28ee1d1c01c29c86d3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <mperttunen@nvidia.com>
+
+[ 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 <mperttunen@nvidia.com>
+Patchwork: https://patchwork.freedesktop.org/patch/719685/
+Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com>
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d94a33d019f0a9bfd57a0d1b33357956ed1b6ba9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:53:45 +0300
+Subject: drm/msm/snapshot: fix dumping of the unaligned regions
+
+From: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+[ 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 <sgaud@qti.qualcomm.com>
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+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 <robin.clark@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 67f19dca447bdb3b9826a64785097e7c6a5035ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 09:27:40 -0800
+Subject: drm/xe: Consolidate workaround entries for Wa_14019988906
+
+From: Matt Roper <matthew.d.roper@intel.com>
+
+[ 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 <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260220-forupstream-wa_cleanup-v2-18-b12005a05af6@intel.com
+Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
+Stable-dep-of: a4660bd94973 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 60ded77fbfee61cc2f6f4808f8fb9f37b83a1c68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Feb 2026 09:27:41 -0800
+Subject: drm/xe: Consolidate workaround entries for Wa_18033852989
+
+From: Matt Roper <matthew.d.roper@intel.com>
+
+[ 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 <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260220-forupstream-wa_cleanup-v2-19-b12005a05af6@intel.com
+Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
+Stable-dep-of: a4660bd94973 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 499fdec8835dd27880cf1ec7882d04d4816e352e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 18:44:45 -0300
+Subject: drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN1
+
+From: Gustavo Sousa <gustavo.sousa@intel.com>
+
+[ 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 <matthew.d.roper@intel.com>
+Link: https://patch.msgid.link/20260514-rtp-mcr-check-v3-2-30dd47855fee@intel.com
+Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
+(cherry picked from commit a672725fdbfc3ea430130039d677c7dc98d59df8)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d4b96e3d78817c2f823b9b178ce6cdecd986d085 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 18:44:46 -0300
+Subject: drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN4
+
+From: Gustavo Sousa <gustavo.sousa@intel.com>
+
+[ 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 <matthew.d.roper@intel.com>
+Link: https://patch.msgid.link/20260514-rtp-mcr-check-v3-3-30dd47855fee@intel.com
+Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
+(cherry picked from commit 75f65f1a4c06da1d87f28570a9d4cdad28f13360)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0bbf1af3a125df2f579292f2979872ab3c0c3d25 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <daniele.ceraolospurio@intel.com>
+Assisted-by: Claude:claude-opus-4.6
+Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3de313e45bafb4b89cdfcacce2c57113abeeb95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shuicheng.lin@intel.com>
+
+[ 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 <ashutosh.dixit@intel.com>
+Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com
+Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
+(cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 26428b3f576f3ae8caa28f14127ecad41a81b1be Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 23:19:18 +0530
+Subject: drm/xe/pf: Fix CFI failure in debugfs access
+
+From: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+
+[ 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 <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
+Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com
+(cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4699c185832a5166da3b10681aa30fe337d4b70a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.d.roper@intel.com>
+
+[ 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 <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260224235055.3038710-2-matthew.d.roper@intel.com
+Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
+Stable-dep-of: 6df5678b6a94 ("drm/xe: Define and use MCR version of COMMON_SLICE_CHICKEN4")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d9a1c6e4e1889267987c14d2a98ff5ff8883a87f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 17:57:26 +0200
+Subject: drm/xe/vf: Fix signature of print functions
+
+From: Michal Wajdeczko <michal.wajdeczko@intel.com>
+
+[ 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 <michal.wajdeczko@intel.com>
+Cc: Mohanram Meenakshisundaram <mohanram.meenakshisundaram@intel.com>
+Reviewed-by: Shuicheng Lin <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com
+(cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ce2727e0ed7c0603bedb966dc9b3291144f8c2e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 12:34:31 +0800
+Subject: erofs: fix managed cache race for unaligned extents
+
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+
+[ Upstream commit 649932fc3815eda2f24eb4de4b3a5e94886ee0b9 ]
+
+After unaligned compressed extents were introduced, the following race
+could occur:
+
+[Thread 1] [Thread 2]
+(z_erofs_fill_bio_vec)
+<handle a Z_EROFS_PREALLOCATED_FOLIO folio>
+...
+filemap_add_folio (1)
+ (z_erofs_bind_cache)
+ <the same folio is found..>
+ ..
+ ..
+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 <avkrasnov@salutedevices.com>
+Closes: https://lore.kernel.org/r/4a2f3801-fac1-42fe-ae75-da315822e088@salutedevices.com
+Tested-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0a387e85aea2ec10c18a337559c07de479384d41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 12:46:07 +0800
+Subject: erofs: fix metabuf leak in inode xattr initialization
+
+From: Jia Zhu <zhujia.zj@bytedance.com>
+
+[ 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 <zhujia.zj@bytedance.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Fixes: bb88e8da0025 ("erofs: use meta buffers for xattr operations")
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2a68dc62594fd74066cc1b34c0ace3eb88d95548 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 17 Mar 2026 15:24:39 +0000
+Subject: erofs: harden h_shared_count in erofs_init_inode_xattrs()
+
+From: Utkal Singh <singhutkal015@gmail.com>
+
+[ 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 <singhutkal015@gmail.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Stable-dep-of: 79b09c54c656 ("erofs: fix metabuf leak in inode xattr initialization")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e8d969a36db3b863d7f82298031ed3c2118d4a08 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:43:43 +0800
+Subject: ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics
+
+From: Chenguang Zhao <zhaochenguang@kylinos.cn>
+
+[ 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 <zhaochenguang@kylinos.cn>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 24e69e8d2920d40811cea171e2e219a06ed9c655 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:33 +0100
+Subject: firmware: arm_ffa: Align RxTx buffer size before mapping
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sebastianene@google.com>
+Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com
+Reviewed-by: Sebastian Ene <sebastianene@google.com>
+Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org
+Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b769c0582ad7bffa513fd39217790730b96f4580 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:30 +0100
+Subject: firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From efb3ba2d9e71bff8a3489981d749ad08af30bfed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bff69d857c5ca4e37c9a7765dd7202c9e463c0d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:28 +0100
+Subject: firmware: arm_ffa: Fix per-vcpu self notifications handling in
+ workqueue
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 844dc4db6a86ec2c0a2b39d8e5681fa8ab0fb4a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:35 +0100
+Subject: firmware: arm_ffa: Fix sched-recv callback partition lookup
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c93ef346c250bdee74d8aa26312f275e4ac0029e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:31 +0100
+Subject: firmware: arm_ffa: Keep framework RX release under lock
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 70bb3aaeabb49527f155571bc60f11a9f93e3b69 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:26 +0100
+Subject: firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 82e434f99d1aab278335ed3452fcbf3093f1282a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:34 +0100
+Subject: firmware: arm_ffa: Snapshot notifier callbacks under lock
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 016d4b194c1e4dd4442aeb7f79b7c2088b89993e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ad39e7a3c5456651c31a7d7c29caff36557da3c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 19:33:32 +0100
+Subject: firmware: arm_ffa: Validate framework notification message layout
+
+From: Sudeep Holla <sudeep.holla@kernel.org>
+
+[ 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 <sudeep.holla@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b3984f777571b630f7e7adb4e4f518076ac4d8f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 16:46:29 +0900
+Subject: fprobe: Fix unregister_fprobe() to wait for RCU grace period
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+[ 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) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ea51abd81fd9da939fd3d86c2efc689f4fe10c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 13:19:18 +0200
+Subject: fs: fix forced iversion increment on lazytime timestamp updates
+
+From: Pankaj Raghav <p.raghav@samsung.com>
+
+[ 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 <p.raghav@samsung.com>
+Link: https://patch.msgid.link/20260511111918.1793689-1-p.raghav@samsung.com
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7e6951bb185358d0458d3b037989ccc9beabc97b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 1 May 2026 15:10:58 +0800
+Subject: fs: Fix return in jfs_mkdir and orangefs_mkdir
+
+From: Hongling Zeng <zenghongling@kylinos.cn>
+
+[ 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 <zenghongling@kylinos.cn>
+Link: https://patch.msgid.link/20260501071058.1243245-1-zenghongling@kylinos.cn
+Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 36486ccd42b001b6e76ca4696a3e2a71b3486e9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <graypanda.inzag@gmail.com>
+
+[ 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 <graypanda.inzag@gmail.com>
+Link: https://patch.msgid.link/20260504112649.1862936-1-graypanda.inzag@gmail.com
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e54799d8fed989231894d10c78e6e40a3714c8c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 14 Mar 2026 14:24:56 +0100
+Subject: gcc-plugins: Always define CONST_CAST_GIMPLE and CONST_CAST_TREE
+
+From: Kees Cook <kees@kernel.org>
+
+[ 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 <ingo@hannover.ccc.de>
+Link: https://lore.kernel.org/lkml/ab6OKoay0OWkywjK@spatz.zoo
+Fixes: 6b90bd4ba40b ("GCC plugin infrastructure")
+Tested-by: Ivan Bulatovic <combuster@archlinux.us>
+Tested-by: Christopher Cradock <christopher@cradock.myzen.co.uk>
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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<gimple>((X))
++#undef CONST_CAST_TREE
++#define CONST_CAST_TREE(X) const_cast<tree>((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
+
--- /dev/null
+From 9cc1563a9107470c4874297d97f9d3c6a14a859d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 10:49:11 +0200
+Subject: gpio: aggregator: fix a potential use-after-free
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260520084911.27938-1-bartosz.golaszewski@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f09399a24b715fd327b456966c29efe9ab68ac7d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 11:53:18 +0200
+Subject: gpio: aggregator: lock device when calling device_is_bound()
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6ea3583c02a18fbeff634c32beb86eeadb8660cf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 14:16:31 +0200
+Subject: gpio: aggregator: remove the software node when deactivating the
+ aggregator
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <geert@linux-m68k.org>
+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 <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 26295d0597ba58d958ea3961d636edebf1b86f8a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Mar 2026 11:31:12 +0100
+Subject: gpio: aggregator: stop using dev-sync-probe
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260327-gpio-kill-dev-sync-probe-v1-2-efac254f1a1d@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Stable-dep-of: 61fef83f239e ("gpio: aggregator: remove the software node when deactivating the aggregator")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/gpio/forwarder.h>
+ #include <linux/gpio/machine.h>
+
+-#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
+
--- /dev/null
+From d18970fcf380d7932a05ffada67f1f3ca20f52f5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:42:16 +0200
+Subject: gpio: cdev: check if uAPI v2 config attributes are correctly zeroed
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <warthog618@gmail.com>
+Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a9d64c72fe7eb783f45f265637b6ff5e46eaf195 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Apr 2026 10:10:02 +0300
+Subject: HID: intel-thc-hid: Intel-quickspi: Fix some error codes
+
+From: Dan Carpenter <error27@gmail.com>
+
+[ 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 <error27@gmail.com>
+Reviewed-by: Even Xu <even.xu@intel.com>
+Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7fc4071250a1a86315dfc4192d37f9f83a7a451e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 5 Feb 2026 09:11:31 +0100
+Subject: HID: quirks: really enable the intended work around for appledisplay
+
+From: Lukas Bulwahn <lukas.bulwahn@redhat.com>
+
+[ 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 <lukas.bulwahn@redhat.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0c9ecca55989abb66652914ca51e253b3f5485d2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 10:33:16 +0200
+Subject: HID: uclogic: Fix regression of input name assignment
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ 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 <tiwai@suse.de>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3aa0500c25815a0d3c946812e92dd0cfc8c5ef75 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 14:41:00 -0700
+Subject: hwmon: (lm90) Add lock protection to lm90_alert
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7971f3e0a9a49409764dfd7ec11dd11c866f73e9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 14:31:49 -0700
+Subject: hwmon: (lm90) Stop work before releasing hwmon device
+
+From: Guenter Roeck <linux@roeck-us.net>
+
+[ 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 <sashiko-bot@kernel.org>
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e4882fc1a390d834087a1dbbd62cb2999179975c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:17 -0700
+Subject: ice: dpll: fix misplaced header macros
+
+From: Ivan Vecera <ivecera@redhat.com>
+
+[ 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 <ivecera@redhat.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-8-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From abc89ac92a8d9f6f36bd2536fb6d36292d6c2ed9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:16 -0700
+Subject: ice: dpll: fix rclk pin state get for E810
+
+From: Ivan Vecera <ivecera@redhat.com>
+
+[ 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 <ivecera@redhat.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-7-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4fd6c4183c2b9f2bb6f272b5102bdc74bdb13fc6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:15 -0700
+Subject: ice: fix locking in ice_dcb_rebuild()
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ 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 <bvanassche@acm.org>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Tested-by: Arpana Arland <arpanax.arland@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a280c91b0be6dde3f9ab24a51467a75df62bfcbf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 14:48:14 -0700
+Subject: ice: fix setting RSS VSI hash for E830
+
+From: Marcin Szycik <marcin.szycik@linux.intel.com>
+
+[ 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 <aleksandr.loktionov@intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 30d8f0e267597f39f03676fd192054da54437209 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:11 -0700
+Subject: ice: ptp: serialize E825 PHY timer start with PTP lock
+
+From: Grzegorz Nitka <grzegorz.nitka@intel.com>
+
+[ 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 <Arkadiusz.kubalewski@intel.com>
+Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Alexander Nowlin <alexander.nowlin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fdcad553fd204fd145862411d472d9fca434c517 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:12 -0700
+Subject: ice: ptp: use primary NAC semaphore on E825
+
+From: Grzegorz Nitka <grzegorz.nitka@intel.com>
+
+[ 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 <Arkadiusz.kubalewski@intel.com>
+Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Tested-by: Alexander Nowlin <alexander.nowlin@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-6-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8c0fd6f0567995751f4b01f4d6d7cd08c20c6443 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <emil.s.tantilov@intel.com>
+
+[ 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] <TASK>
+[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] </TASK>
+
+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 <emil.s.tantilov@intel.com>
+Reviewed-by: Madhu Chittim <madhu.chittim@intel.com>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Samuel Salin <Samuel.salin@intel.com>
+Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-3-a5ea4dc837a9@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 726e1d9f0b2d2070df716ca3391e3b0ef07af32d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 11:24:15 -0700
+Subject: igc: set tx buffer type for SMD frames
+
+From: Kohei Enju <kohei@enjuk.jp>
+
+[ 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 <kohei@enjuk.jp>
+Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Avigail Dahan <avigailx.dahan@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Link: https://patch.msgid.link/20260515182419.1597859-9-anthony.l.nguyen@intel.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 113abcffa9fa3d7efd2925eff93cdfda6da94bd6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <axboe@kernel.dk>
+
+[ 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 <krisman@suse.de>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/file.h>
+ #include <linux/slab.h>
+ #include <linux/net.h>
++#include <linux/un.h>
+ #include <linux/compat.h>
+ #include <net/compat.h>
+ #include <linux/io_uring.h>
+@@ -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
+
--- /dev/null
+From 7265490bd4fdbf2ad63e2fc2625e9f68f3da40ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:00:44 +0200
+Subject: io_uring/nop: pass all errors to userspace
+
+From: Alexander A. Klimov <grandmaster@al2klimov.de>
+
+[ 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 <grandmaster@al2klimov.de>
+Link: https://patch.msgid.link/20260520180045.538533-1-grandmaster@al2klimov.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 518631ee8cd994ce89a2f0da5143f8dcbfc914c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 May 2026 17:30:10 -0400
+Subject: io_uring: propagate array_index_nospec opcode into req->opcode
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ 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 <michael.bommarito@gmail.com>
+Link: https://patch.msgid.link/20260517213010.696135-1-michael.bommarito@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ed1b2addc9a89c67c6af1424b37ff2ff9c0225d0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:46:13 -0300
+Subject: iommu: Fix loss of errno on map failure for classic ops
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <josua@solid-run.com>
+Closes: https://lore.kernel.org/all/321c2e57-6a17-4aef-ba42-d2ebd577e472@solid-run.com/
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Reviewed-by: Mostafa Saleh <smostafa@google.com>
+Tested-by: Josua Mayer <josua@solid-run.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b1a36321d95b8014d62b6378faa9beb2beee77a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:46:14 -0300
+Subject: iommu: Fix up map/unmap debugging for iommupt domains
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <jgg@nvidia.com>
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Reviewed-by: Mostafa Saleh <smostafa@google.com>
+Tested-by: Josua Mayer <josua@solid-run.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fb3146fa7943f7527d010f8fb4059d75ee8425ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:46:15 -0300
+Subject: iommu: Handle unmap error when iommu_debug is enabled
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <jgg@nvidia.com>
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Reviewed-by: Mostafa Saleh <smostafa@google.com>
+Tested-by: Josua Mayer <josua@solid-run.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 99589b27fb3fe1bf0f9c0b255eec92f9ca23c50f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Feb 2026 15:30:11 -0400
+Subject: iommupt: Avoid rewalking during map
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <skhawaja@google.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8370e4a8ae7bcf0aaed54406874edc792cd20b5a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:46:16 -0300
+Subject: iommupt: Check for missing PAGE_SIZE in the pgsize_bitmap
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <jgg@nvidia.com>
+Reviewed-by: Pranjal Shrivastava <praan@google.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Tested-by: Josua Mayer <josua@solid-run.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d23de98738ab4db31f9406e6b8dfc39c429c4663 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Feb 2026 15:30:10 -0400
+Subject: iommupt: Directly call iommupt's unmap_range()
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <skhawaja@google.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 0735c54804c7 ("iommu: Handle unmap error when iommu_debug is enabled")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/sched/mm.h>
+ #include <linux/msi.h>
+ #include <uapi/linux/iommufd.h>
++#include <linux/generic_pt/iommu.h>
+
+ #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
+
--- /dev/null
+From 86f893482f55274ac6dd93523945ae1d168f3faa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:46:17 -0300
+Subject: iommupt: Fix the end_index calculation in __map_range_leaf()
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ 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 <jgg@nvidia.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Reviewd-by: Pranjal Shrivastava <praan@google.com>
+Tested-by: Josua Mayer <josua@solid-run.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 80ac00ad332c79940031c29ab8e98a01bd8a8c04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <jiayuan.chen@linux.dev>
+
+[ 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 <bigeasy@linutronix.de>
+Suggested-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9f78ee7471d1b0933567dbb722012ff11147f6b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 01:55:22 -0700
+Subject: irqchip/ath79-cpu: Remove unused function
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <lkp@intel.com>
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+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 <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 07f1841f0714057cb66047375a354b66e00b64d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <viktor_jaegerskuepper@freenet.de>
+
+[ 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 <linux@weissschuh.net>
+Signed-off-by: Viktor Jägersküpper <viktor_jaegerskuepper@freenet.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Tested-by: Nathan Chancellor <nathan@kernel.org>
+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 <nsc@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7b4daacd10ed0131a496dc46863df33c4b5b2074 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 01:16:05 +0000
+Subject: kho: skip KHO for crash kernel
+
+From: Evangelos Petrongonas <epetron@amazon.de>
+
+[ 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 <epetron@amazon.de>
+Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Link: https://patch.msgid.link/20260410011609.1103-1-epetron@amazon.de
+Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fced19eb2195cf2aed36b9b3b37939528340ed93 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
+
+From: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
+
+[ 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 <jianpeng.chang.cn@windriver.com>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a94dd74efc8a0afe4f00b5320b257d2a06aa4778 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 23:27:19 +0900
+Subject: ksmbd: fix durable reconnect error path file lifetime
+
+From: Junyi Liu <moss80199@gmail.com>
+
+[ 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 <moss80199@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ffa025edecf953723bdd35dd2ff9ae40750276cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:53 +0800
+Subject: kunit: config: Enable KUNIT_DEBUGFS by default
+
+From: David Gow <david@davidgow.net>
+
+[ 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 <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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/<test_suite>/results files for each
+--
+2.53.0
+
--- /dev/null
+From ee50d40761d2e2f8ff34134b9ddc6c686aee439a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Apr 2026 11:41:54 +0800
+Subject: kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
+
+From: David Gow <david@davidgow.net>
+
+[ 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/<suite>/results display")
+Signed-off-by: David Gow <david@davidgow.net>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 09d12455050c5560c125b9e19d6276c9072850e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:05:07 +0800
+Subject: LoongArch: kprobes: Fix handling of fatal unrecoverable recursions
+
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+
+[ 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 <yangtiezhu@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6dab2eaeb20e7cb97a202b69a66ba9638e6ae438 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
+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) <rppt@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dcd20b6314bf1422470355f9a569b1aeed0aa285 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 May 2026 14:26:16 -0700
+Subject: net: ag71xx: check error for platform_get_irq
+
+From: Rosen Penev <rosenp@gmail.com>
+
+[ 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 <rosenp@gmail.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8aaf78786d0fb00cea8809bb199432c0625ef8e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 15:12:02 +0200
+Subject: net: airoha: Disable GDM2 forwarding before configuring GDM2 loopback
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+[ 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 <madhur.agrawal@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 786508ca50476dd7bd45b77a1d3461e91e335c5e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 15:44:57 +0200
+Subject: net: airoha: Fix NPU RX DMA descriptor bits
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ 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 <ansuelsmth@gmail.com>
+Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://patch.msgid.link/20260518134530.3683-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 215ae3bd8a4ab83a7bd76fc4d931945bcd8742b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8c85e18893f01d91fab1014f699df3b2d0e7f4af Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 15:04:35 +0100
+Subject: net: dsa: mt7530: preserve VLAN tags on trapped link-local frames
+
+From: Daniel Golle <daniel@makrotopia.org>
+
+[ 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 <daniel@makrotopia.org>
+Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
+Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5ac277a2582278ebdfaa7a3fe54a4631e0842134 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <wei.fang@nxp.com>
+
+[ 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 <wei.fang@nxp.com>
+Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
+Link: https://patch.msgid.link/20260520064421.91569-3-wei.fang@nxp.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2d20e4722cb9e67592057e79dbbbadff99a6cb1b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:38 +0200
+Subject: net: ethernet: cortina: Carry over frag counter
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From afc26710ac5cd7d94901ab04910ebcaa42d001b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <eitschman@nebelreich.de>
+
+[ 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 <eitschman@nebelreich.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: ebd8ec2b309e ("net: ethernet: cortina: Carry over frag counter")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f0a0f444746dcb338877887abbd07b01b4d566ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 May 2026 00:13:37 +0200
+Subject: net: ethernet: cortina: Make RX SKB per-port
+
+From: Linus Walleij <linusw@kernel.org>
+
+[ 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 <linusw@kernel.org>
+Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a162b2d33fc671ff093e74e183b7f9511be5c031 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 19:37:28 -0700
+Subject: net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
+
+From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
+
+[ 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 <enelsonmoore@gmail.com>
+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 <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a0124f8f241371e893db34099c360106053059c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 22:44:42 +0200
+Subject: net: gro: don't merge zcopy skbs
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ 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 <huzaifas@redhat.com>
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d9e86f33159eb4d158f2376dfa8ffe20549e1a5a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 21:43:11 +0900
+Subject: net: lan966x: avoid unregistering netdev on register failure
+
+From: Myeonghun Pak <mhun512@gmail.com>
+
+[ 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 <ae878000@gmail.com>
+Signed-off-by: Ijae Kim <ae878000@gmail.com>
+Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
+Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 23371111c2dc3dbe488ce85c264563c34608167d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ernis@linux.microsoft.com>
+
+[ 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 <ernis@linux.microsoft.com>
+Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 000b356ab8cfc2db9b28f2ba5effbf79896950a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <gargaditya@linux.microsoft.com>
+
+[ 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 <gargaditya@linux.microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 54d4601186bdd724ea2094596247825897214167 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:33:02 +0300
+Subject: net/mlx5: Do not restore destination-less TC rules
+
+From: Jeroen Massar <jmassar@nvidia.com>
+
+[ 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 <jmassar@nvidia.com>
+Reviewed-by: Jianbo Liu <jianbol@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b114563126be3af31d9ed51d0a68c60ee7541d61 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:36:40 +0300
+Subject: net/mlx5: Skip disabled vports when setting max TX speed
+
+From: Or Har-Toov <ohartoov@nvidia.com>
+
+[ 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 <ohartoov@nvidia.com>
+Reviewed-by: Mark Bloch <mbloch@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260513063640.334132-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From b14641ae7cc5e0952e9e3f0b7acdca2ad9cd4f89 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 23:59:00 +0100
+Subject: net/mlx5e: Fix eswitch mode block underflow on IPsec acquire SA
+
+From: Prathamesh Deshpande <prathameshdeshpande7@gmail.com>
+
+[ 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 <prathameshdeshpande7@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Tariq Toukan <tariqt@nvidia.com>
+Link: https://patch.msgid.link/20260510225903.13184-1-prathameshdeshpande7@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ce85096803736fdc420c9b503b41d368c7979101 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 09:46:13 +0300
+Subject: net/mlx5e: xsk: Fix unlocked writing to ICOSQ
+
+From: Dragos Tatulea <dtatulea@nvidia.com>
+
+[ 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:
+ <IRQ>
+ 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
+ </IRQ>
+ <TASK>
+ 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 <ps@mu.org>
+Signed-off-by: Dragos Tatulea <dtatulea@nvidia.com>
+Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260513064613.334602-1-tariqt@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 38dce8586a574f70bc57936d44774c494e090d0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 09:08:08 +0000
+Subject: net: napi: Avoid gro timer misfiring at end of busypoll
+
+From: Dragos Tatulea <dtatulea@nvidia.com>
+
+[ 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 <mkarsten@uwaterloo.ca>
+Signed-off-by: Martin Karsten <mkarsten@uwaterloo.ca>
+Signed-off-by: Dragos Tatulea <dtatulea@nvidia.com>
+Reviewed-by: Tariq Toukan <tariqt@nvidia.com>
+Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
+Reviewed-by: Joe Damato <joe@dama.to>
+Link: https://patch.msgid.link/20260506090808.820559-2-dtatulea@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4f73884d5be44eeed529941f1c9dfd863f8f81f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 09:19:47 +0200
+Subject: net: phy: DP83TC811: add reading of abilities
+
+From: Sven Schuchmann <schuchmann@schleissheimer.de>
+
+[ 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 <andrew@lunn.ch>
+Signed-off-by: Sven Schuchmann <schuchmann@schleissheimer.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de
+[pabeni@redhat.com: dropped revision history]
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a0c74398198f247261ad78ead79548856e7a5226 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:23:10 +0200
+Subject: net: phy: honor eee_disabled_modes in phy_advertise_eee_all()
+
+From: Nicolai Buchwitz <nb@tipi-net.de>
+
+[ 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 <nb@tipi-net.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-2-05b52626fa68@tipi-net.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 903d5b5174dcc9f810f987f1f1c0e34eb12cc15b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:23:09 +0200
+Subject: net: phy: honor eee_disabled_modes in phy_support_eee()
+
+From: Nicolai Buchwitz <nb@tipi-net.de>
+
+[ 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 <nb@tipi-net.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-1-05b52626fa68@tipi-net.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d36c15a7d9b8531cd8f3a7fc5f27b9293fc48c2b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 15:13:24 -0700
+Subject: net: shaper: annotate the data races
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <horms@kernel.org>
+Link: https://patch.msgid.link/20260515221325.1685455-2-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: b8d7519352ba ("net: shaper: rework the VALID marking (again)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4c83863bafc189f07c692004f8ff59ba48f50395 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:03 -0700
+Subject: net: shaper: enforce singleton NETDEV scope with id 0
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e14a4d58f61a67c9cd664bb74a31568a5ef009d9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:56 -0700
+Subject: net: shaper: fix trivial ordering issue in net_shaper_commit()
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 17cce851a28b5595a93ffd109d3b76e8ecd285bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:00 -0700
+Subject: net: shaper: fix undersized reply skb allocation in GROUP command
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dd26f534ccb63f63925fd0dc51eac2860602f16c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:55 -0700
+Subject: net: shaper: flip the polarity of the valid flag
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f96f73a54bee4aefe5e2660b3febdd947c33acfc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:28:57 -0700
+Subject: net: shaper: reject duplicate leaves in GROUP request
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-4-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b633a86e382ed381c1efad0c8723f68ffe990a2a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:02 -0700
+Subject: net: shaper: reject handle IDs exceeding internal bit-width
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-9-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <uapi/linux/net_shaper.h>
+
++/* 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 <uapi/linux/net_shaper.h>
+
++#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
+
--- /dev/null
+From bf339d36e5bb44703b3c28f002c6ed96bac1be4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 12:29:04 -0700
+Subject: net: shaper: reject QUEUE scope handle with missing id
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e62cedcbebb009a7f6acbbd979399b1133ee389b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 16:37:45 -0700
+Subject: net: shaper: Reject reparenting of existing nodes
+
+From: Mohsin Bashir <hmohsin@meta.com>
+
+[ 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 <hmohsin@meta.com>
+Link: https://patch.msgid.link/20260506233745.111895-1-mohsin.bashr@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d9e41acc701c2d474296d734a2f8576edfd06721 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 15:13:25 -0700
+Subject: net: shaper: rework the VALID marking (again)
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations")
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260515221325.1685455-3-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9a533711fad1a0d2897b79896b62613e63fe3bc0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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 <kuba@kernel.org>
+Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b6b57191df31c014994efcb42f16a576438dd496 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
+Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9780be2e426a8ed8c1bebf8c5061c13b33ea7bf1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xmei5@asu.edu>
+
+[ 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 <bestswngs@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Xiang Mei <xmei5@asu.edu>
+Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f802bfb721fe730f0ccc61a0911c12e6b04c4676 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:21:37 +0800
+Subject: net: stmmac: eswin: clear TXD and RXD delay registers during
+ initialization
+
+From: Zhi Li <lizhi2@eswincomputing.com>
+
+[ 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 <lizhi2@eswincomputing.com>
+Link: https://patch.msgid.link/20260518022137.464-1-lizhi2@eswincomputing.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From a1eff9f44780e22c59cfa25e70fe37cd9197a039 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:21:52 +0800
+Subject: net: stmmac: eswin: correct RGMII delay granularity to 20 ps
+
+From: Zhi Li <lizhi2@eswincomputing.com>
+
+[ 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 <lizhi2@eswincomputing.com>
+Link: https://patch.msgid.link/20260518022156.484-1-lizhi2@eswincomputing.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 89cb3b1a6b21cfe0f9a105ee4b2c5012c7868347 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:20:55 +0800
+Subject: net: stmmac: eswin: fix HSP CSR init ordering after clock enable
+
+From: Zhi Li <lizhi2@eswincomputing.com>
+
+[ 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 <lizhi2@eswincomputing.com>
+Link: https://patch.msgid.link/20260518022055.444-1-lizhi2@eswincomputing.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From d9d7f33601235d0b617a3d9c64af02b65b2ff84f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 10:22:13 +0800
+Subject: net: stmmac: eswin: validate RGMII delay values
+
+From: Zhi Li <lizhi2@eswincomputing.com>
+
+[ 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 <lizhi2@eswincomputing.com>
+Link: https://patch.msgid.link/20260518022214.507-1-lizhi2@eswincomputing.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From 38bd4176a3172451766fc0884f0af19ad50e3b94 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 01:28:13 +0530
+Subject: net: ti: icssm-prueth: fix eth_ports_node leak in probe
+
+From: Shitalkumar Gandhi <shital.gandhi45@gmail.com>
+
+[ 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 <shitalkumar.gandhi@cambiumnetworks.com>
+Link: https://patch.msgid.link/20260506195813.641610-1-shitalkumar.gandhi@cambiumnetworks.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1c37e6d4896bd46740ad7c8890b972a7b7e2aa31 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuba@kernel.org>
+
+[ 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: é’±ä¸€é“ <yimingqian591@gmail.com>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 115e43fff117cdffa7a8a3e1199d5f10ff1b28c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 10:49:18 -0700
+Subject: net: tls: prevent chain-after-chain in plain text SG
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ 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 <sashiko-bot@kernel.org>
+Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ec05f736b2ab53484c52f2546aa3385e67c55e88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 11:19:22 +0200
+Subject: netfilter: bridge: eb_tables: close module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e3e7add162ad53e6d66c5022c60183e89b33ccc5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:19 +0200
+Subject: netfilter: ebtables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d20e516586eb183700ba5f780af1e2dda6d5f487 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:18 +0200
+Subject: netfilter: ebtables: move to two-stage removal scheme
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9826af9cb45e322f81550b3f98ce549a3f3539d6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 13:00:28 +0200
+Subject: netfilter: nf_conntrack_expect: restore helper propagation via
+ expectation
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ 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 <i.maximets@ovn.org>
+Tested-by: Ilya Maximets <i.maximets@ovn.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3ccacb2ec192db09504d6489995b8b9b8ad90827 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 11:30:49 +0200
+Subject: netfilter: nft_inner: release local_lock before re-enabling softirqs
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e0400b15717a478dbee83d7dd6cca013b1b71ee4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:15 +0200
+Subject: netfilter: x_tables: add and use xt_unregister_table_pre_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 60c951b1feab6188cfc82dfb73caa2a1a0223587 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:17 +0200
+Subject: netfilter: x_tables: add and use xtables_unregister_table_exit
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 536643e3a80c57f653b90609ddaee19593d323ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:14 +0200
+Subject: netfilter: x_tables: allocate hook ops while under mutex
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e041810f708cabcdf34162d0493635c94d16dd37 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: b62eb8dcf2c4 ("netfilter: x_tables: allocate hook ops while under mutex")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 75ab2c1397839b84fe9218a85af1ab749179eedf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:20 +0200
+Subject: netfilter: x_tables: close dangling table module init race
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2f1ee523a7603dce736fb467b6a2e99c3dff3215 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 12:07:16 +0200
+Subject: netfilter: x_tables: unregister the templates first
+
+From: Florian Westphal <fw@strlen.de>
+
+[ 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 <tristan@talencesecurity.com>
+Reviewed-by: Tristan Madani <tristan@talencesecurity.com>
+Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ee9a511c938f8f358feae301320e948f774d6cab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:34:00 +0100
+Subject: netfs, afs: Fix write skipping in dir/link writepages
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-24-dhowells@redhat.com
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b379adadad115172aa3f3147cd0e035c6834bb51 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:49 +0100
+Subject: netfs: Defer the emission of trace_netfs_folio()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 630a2608b51d1857304074b5f75d933e1ae6cf55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:38 +0100
+Subject: netfs: Fix cancellation of a DIO and single read subrequests
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-2-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0bd0524e0a9e00158e97e0ab8bc05e74d6c0a810 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:54 +0100
+Subject: netfs: Fix early put of sink folio in netfs_read_gaps()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <sfrench@samba.org>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 649c8c31fd6698cd93cdbe347d407896f22c36f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:58 +0100
+Subject: netfs: Fix folio->private handling in netfs_perform_write()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/pagevec.h>
+ #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
+
--- /dev/null
+From b012a781bf495c80479b2b74d2713c367af07c53 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:55 +0100
+Subject: netfs: Fix leak of request in netfs_write_begin() error handling
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-19-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b5346a3c66913d4c95954d28e3f5151f2ffe1be3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:40 +0100
+Subject: netfs: Fix missing barriers when accessing stream->subrequests
+ locklessly
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-4-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 117011aa9b0b2570effa24a78a29f2a058a8e89b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:39 +0100
+Subject: netfs: Fix missing locking around retry adding new subreqs
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-3-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 842ccc7439f68cc931b8de7d19aba071adc73a0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <marc.dionne@auristor.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a0ea356af5404b2f593dacd7079a349f92a0fb5d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:59 +0100
+Subject: netfs: Fix netfs_read_folio() to wait on writeback
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-23-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c2532420d481ce31028129917c9c483f1af76fb2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:41 +0100
+Subject: netfs: Fix netfs_read_to_pagecache() to pause on subreq failure
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-5-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f1c4af3330a5da9dda677ba3d13ec9e44f01d2b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:47 +0100
+Subject: netfs: Fix overrun check in netfs_extract_user_iter()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4528b59914432811499d716070508507db52b504 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:57 +0100
+Subject: netfs: Fix partial invalidation of streaming-write folio
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1ec6073d6384587bd68c4f89161d4bd1590ce09b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:51 +0100
+Subject: netfs: Fix potential deadlock in write-through mode
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5e1b996297965fd302dfb2f5ab5fdda49e938744 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-6-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c16806a55a5d78f632fb02b6f265ba275223f481 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:56 +0100
+Subject: netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fb8156c58fee4b286c9d18b1bf78c13f5638a2ab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:52 +0100
+Subject: netfs: Fix read-gaps to remove netfs_folio from filled folio
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-16-dhowells@redhat.com
+Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading")
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6d85e83092b08923099bf35f47af6d564c9448f7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:50 +0100
+Subject: netfs: Fix streaming write being overwritten
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From efa512d1fc690eaf228b29acf4192f8e6ff66c3a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <Slava.Dubeyko@ibm.com>
+
+[ 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] <TASK>
+[ 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] </TASK>
+[ 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 <Slava.Dubeyko@ibm.com>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+cc: Ceph Development <ceph-devel@vger.kernel.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4dd51a2a996314e9ca7e20d6210f8d9af16d65bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:53 +0100
+Subject: netfs: Fix write streaming disablement if fd open O_RDWR
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 1035ae949ecfa930bb5812d3578aa11f0e4d9e02 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 May 2026 13:33:43 +0100
+Subject: netfs: Fix zeropoint update where i_size > remote_i_size
+
+From: David Howells <dhowells@redhat.com>
+
+[ 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 <dhowells@redhat.com>
+Link: https://patch.msgid.link/20260512123404.719402-7-dhowells@redhat.com
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Matthew Wilcox <willy@infradead.org>
+cc: netfs@lists.linux.dev
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7c74be228873f7379973da6d32886a77b257d7d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Apr 2026 14:52:59 -0400
+Subject: NFSD: Fix infinite loop in layout state revocation
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <dai.ngo@oracle.com>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Tested-by: Dai Ngo <dai.ngo@oracle.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ce34e2b1cfe7cae2deff488956f6c9a1dbfc7edb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 May 2026 19:23:01 +0800
+Subject: nsfs: fix wrong error code returned for pidns ioctls
+
+From: Zhihao Cheng <chengzhihao1@huawei.com>
+
+[ 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 <chengzhihao1@huawei.com>
+Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com
+Reviewed-by: Yang Erkun <yangerkun@huawei.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3909e888120bba30b41fa42b50b57072e5b6bcf9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 06:16:02 -0700
+Subject: nvme: fix bio leak on mapping failure
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <sagi@grimberg.me>
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f96afb930a99144a77566ce8d02b42acf2f1440b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 13:01:57 -0700
+Subject: nvme-pci: fix dma mapping leak on data setup error
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 192da0cb2051885ee078baccf208b07b8dd8cb3b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 May 2026 18:03:44 -0700
+Subject: nvme-pci: fix dma_vecs leak on p2p memory
+
+From: Keith Busch <kbusch@kernel.org>
+
+[ 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 <hch@lst.de>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ea2b0281a1a7e2bf9a944cba3f3ba870737b6408 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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) <acelan.kao@canonical.com>
+
+[ 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:
+ <TASK>
+ 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
+ </TASK>
+
+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) <acelan.kao@canonical.com>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ebcfa7b6038370e61a94952e16ddc3c30e3e67c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rkannoth@marvell.com>
+
+[ 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 <gakula@marvell.com>
+Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks")
+Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 861a8e683a72694a9c069cf23d341483699a4963 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 15:26:10 +0200
+Subject: ovpn: disable BHs when updating device stats
+
+From: Ralf Lici <ralf@mandelbit.com>
+
+[ 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 <ralf@mandelbit.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 <linux/netdevice.h>
++
+ /* 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
+
--- /dev/null
+From a15514db20324f9888315f8a30245977a95764c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 17 Mar 2026 14:47:56 +0100
+Subject: ovpn: fix race between deleting interface and adding new peer
+
+From: Antonio Quartulli <antonio@openvpn.net>
+
+[ 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 <imv4bel@gmail.com>
+Closes: https://lore.kernel.org/netdev/aaVgJ16edTfQkYbx@v4bel/
+Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
+Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object")
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 62e538fecfdb7970a74e3e7aee9490881861396a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 11:55:21 +0100
+Subject: ovpn: respect peer refcount in CMD_NEW_PEER error path
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <sd@queasysnail.net>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: David Carlier <devnexen@gmail.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From df6119e4bd09e996f8abfc680f2ea89219d36eee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 11:55:20 +0100
+Subject: ovpn: tcp - use cached peer pointer in ovpn_tcp_close()
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <sd@queasysnail.net>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: David Carlier <devnexen@gmail.com>
+Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8368069b0be9c9e19753b54e46e742a85d93af5d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 20:58:42 +0000
+Subject: pds_core: ensure null-termination for firmware version strings
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 35dcf64a9b72e4ee96d62a49ff47b6816c5102ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:07 +0000
+Subject: pds_core: fix debugfs_lookup dentry leak and error handling
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 63a6c530859c51f9df7a10d8c6f2f9f4834171f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 21:29:05 +0000
+Subject: pds_core: fix error handling in pdsc_devcmd_wait
+
+From: Nikhil P. Rao <nikhil.rao@amd.com>
+
+[ 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 <nikhil.rao@amd.com>
+Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bfc0f86d5128ae518f3bb64a3dc6bf6441a6b556 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 21:19:58 +0100
+Subject: phy: apple: atc: Fix typec switch/mux leak on unbind
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Tested-by: Joshua Peisach <jpeisach@ubuntu.com>
+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 <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From aab14623f2913cfd429f5181583298e5e739b310 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <j4g8y7@gmail.com>
+
+[ 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 <j4g8y7@gmail.com>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 452474a7b864540ca5a565ca32fd6d97d8671613 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <xiangxu.yin@oss.qualcomm.com>
+
+[ 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 <dan.carpenter@linaro.org>
+Fixes: 81791c45c8e0 ("phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY config and DP mode support")
+Signed-off-by: Xiangxu Yin <xiangxu.yin@oss.qualcomm.com>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260227-master-v1-1-8d91b9407fdb@oss.qualcomm.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6bbc833a4bf8376191c4a948fa0e1992be6feba9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 26 Mar 2026 00:23:58 +0800
+Subject: phy: spacemit: Remove incorrect clk_disable() in
+ spacemit_usb2phy_init()
+
+From: Felix Gu <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Reviewed-by: Ze Huang <huang.ze@linux.dev>
+Link: https://patch.msgid.link/20260326-k1-usb3-v1-1-0c2b6adf5185@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c3fec687840321fce5722181c33f002aa1c70dbc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 09:09:35 +0200
+Subject: pinctrl: mediatek: moore: implement gpio_chip::get_direction()
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ 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 <frank-w@public-files.de>
+Closes: https://lore.kernel.org/all/20260409132724.126258-1-linux@fw-web.de/
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Tested-By: Frank Wunderlich <frank-w@public-files.de>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e8f958dcf3278ae90ca9a2c449f5f551fdc041d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Apr 2026 11:44:13 +0000
+Subject: pinctrl: meson: amlogic-a4: fix deadlock issue
+
+From: Xianwei Zhao <xianwei.zhao@amlogic.com>
+
+[ 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 <xianwei.zhao@amlogic.com>
+Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 61f53705cbf2f63f606aeb8e7b2db9989de58654 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Apr 2026 16:55:24 +0530
+Subject: pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6e4bd75ba189ac8dff991f365dd50b7e3cf34b3e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 17:44:58 +0530
+Subject: pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for
+ sm8150
+
+From: Maulik Shah <maulik.shah@oss.qualcomm.com>
+
+[ 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 <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
+Signed-off-by: Navya Malempati <navya.malempati@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b54f0cbc56bd6fbe3990365d6eb0b765cf99e139 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Apr 2026 15:52:34 +0200
+Subject: pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function
+
+From: Til Kaiser <mail@tk154.de>
+
+[ 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 <mail@tk154.de>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Signed-off-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 724e42b37ddf1617dda89b4bfd51e78fed43cf3d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <biju.das.jz@bp.renesas.com>
+
+[ 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 <biju.das.jz@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From c2cb5e0f7a709c85b030283185567603eabbb739 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Apr 2026 19:24:51 +0100
+Subject: pinctrl: renesas: rzg2l: Fix SMT register cache handling
+
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+
+[ 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 <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/20260413182456.811543-2-prabhakar.mahadev-lad.rj@bp.renesas.com
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 7fe9f989d855c9de8a4e7a2ab12bc8e0f34c2482 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <oliverjwhite07@gmail.com>
+
+[ 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 <oliverjwhite07@gmail.com>
+Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bdeb9a72eb50b75f0618f1545a24ce9ab723b2c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b932b2f6089ad55a86f3336f6205bcbe895760dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <yaseen@ghoul.dev>
+
+[ 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 <yaseen@ghoul.dev>
+Reviewed-by: Denis Benato <denis.benato@linux.dev>
+Link: https://patch.msgid.link/20260517182957.11069-1-yaseen@ghoul.dev
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e71b5f42db042c0566e423548fd695e5f0b67bf5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9f5b8618d0ecc03717ef33cc26acd517813a9691 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 64d31dec6cdcfffa7657d102d86ceb18ee8685e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 10ca759bbb9b1b04a8d5b738ec2aa186fd8045e3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <rafael.j.wysocki@intel.com>
+
+[ 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 <rafael.j.wysocki@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/14023870.uLZWGnKmhe@rafael.j.wysocki
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 73258e57e21a71552d14c4a8216c93fe2e6cec67 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <W_Armin@gmx.de>
+
+[ 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 <wse@tuxedocomputers.com>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://patch.msgid.link/20260512232145.329260-3-W_Armin@gmx.de
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 36a69fc1389c001e13d76ac5b0f51396e2c63dc7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <W_Armin@gmx.de>
+
+[ 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 <wse@tuxedocomputers.com>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://patch.msgid.link/20260512232145.329260-5-W_Armin@gmx.de
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 0d1b2f03b431a95559889b39f589c4ea5c9733fb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <W_Armin@gmx.de>
+
+[ 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 <wse@tuxedocomputers.com>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://patch.msgid.link/20260512232145.329260-4-W_Armin@gmx.de
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5aec8935aaf57a90cbaac618f179a157efff9d57 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <W_Armin@gmx.de>
+
+[ 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 <wse@tuxedocomputers.com>
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://patch.msgid.link/20260512232145.329260-2-W_Armin@gmx.de
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4e44da434abb3d129d900121aee350b5d7555dea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 16 Nov 2025 19:55:44 +0530
+Subject: powerpc: 82xx: fix uninitialized pointers with free attribute
+
+From: Ally Heev <allyheev@gmail.com>
+
+[ 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 <dan.carpenter@linaro.org>
+Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/
+Signed-off-by: Ally Heev <allyheev@gmail.com>
+Fixes: 4aa5cc1e0012 ("powerpc-km82xx.c: replace of_node_put() with __free")
+Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
+Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20251116-aheev-uninitialized-free-attr-km82xx-v2-1-4307e2b5300d@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4629713be1234e854f2792f2f128ca777dcb914b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Apr 2026 17:15:45 +0100
+Subject: powerpc: fix dead default for GUEST_STATE_BUFFER_TEST
+
+From: Julian Braha <julianbraha@gmail.com>
+
+[ 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 <julianbraha@gmail.com>
+Tested-by: Gautam Menghani <gautam@linux.ibm.com>
+Reviewed-by: Amit Machhiwal <amachhiw@linux.ibm.com>
+Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a743f794116d95d0a911fdff24e719e183a41c83 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:42:56 +0530
+Subject: powerpc/hv-gpci: fix preempt count leak in sysfs show paths
+
+From: Aboorva Devarajan <aboorvad@linux.ibm.com>
+
+[ 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: <task>/<pid>/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 <aboorvad@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260508041256.3447113-1-aboorvad@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 07f1046793bcc781cb92950337da5641814f61ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <sayalip@linux.ibm.com>
+
+[ 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 <SF,ME,RI,LE> 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 <mahesh@linux.ibm.com>
+Acked-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
+[Maddy: Fixed the commit title]
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From a40c62177717d57f7153a2f757213e8c552fe3ac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <shirazsaleem@microsoft.com>
+
+[ 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 <shirazsaleem@microsoft.com>
+Signed-off-by: Konstantin Taranov <kotaranov@microsoft.com>
+Link: https://patch.msgid.link/20260512094209.264955-1-kotaranov@linux.microsoft.com
+Reviewed-by: Long Li <longli@microsoft.com>
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From b99bedacc5852cbd987d3731fbfefa9471aed4e1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 19:38:34 +0800
+Subject: RDMA/rtrs: Fix use-after-free in path file creation cleanup
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+[ 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 <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com
+Signed-off-by: Leon Romanovsky <leon@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4551eade6573edb903c55a2927a10a6ed0a28a89 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Apr 2026 16:23:04 -0700
+Subject: riscv: Docs: fix unmatched quote warning
+
+From: Randy Dunlap <rdunlap@infradead.org>
+
+[ 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 <rdunlap@infradead.org>
+Link: https://patch.msgid.link/20260406232304.1892528-1-rdunlap@infradead.org
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 365c3ad50d79cc12c5b6392ce0645a2f48a2b3ff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 9 Apr 2026 09:11:39 +0000
+Subject: riscv: errata: Fix bitwise vs logical AND in MIPS errata patching
+
+From: Michael Neuling <mikey@neuling.org>
+
+[ 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 <mikey@neuling.org>
+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 <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f1aeb02785b2df05b6698af0584804dcae898618 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 1 May 2026 06:23:20 +0000
+Subject: riscv: Fix register corruption from uninitialized cregs on error
+
+From: Michael Neuling <mikey@neuling.org>
+
+[ 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 <mikey@neuling.org>
+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 <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cb07da346f9347cd6aa65351546c092cd6980142 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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) <guoren@kernel.org>
+
+[ 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 <alexghiti@rivosinc.com>
+Cc: Björn Töpel <bjorn@rivosinc.com>
+Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
+Tested-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
+Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org
+[pjw@kernel.org: cleaned up commit message]
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 6bb09ec0e619cdb31bf74772a9dcfc9fa42cbb99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <dhowells@redhat.com>
+
+[ 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 <imv4bel@gmail.com>
+Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Simon Horman <horms@kernel.org>
+cc: Jiayuan Chen <jiayuan.chen@linux.dev>
+cc: linux-afs@lists.infradead.org
+Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
+Tested-by: Marc Dionne <marc.dionne@auristor.com>
+Link: https://patch.msgid.link/20260515230516.2718212-3-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 2721f14115b9aaa8e65f69fe29b7813cf71b2f6e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 12:53:17 -0500
+Subject: scsi: sd: Fix return code handling in sd_spinup_disk()
+
+From: Mike Christie <michael.christie@oracle.com>
+
+[ 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 <error27@gmail.com>
+Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u
+Signed-off-by: Mike Christie <michael.christie@oracle.com>
+Reviewed-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 8e21afb72cd6487ceeb5da2f7384b690db800ad7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 18:39:28 +0300
+Subject: selftests: net: Fix checksums in xdp_native
+
+From: Nimrod Oren <noren@nvidia.com>
+
+[ 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 <cjubran@nvidia.com>
+Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
+Signed-off-by: Nimrod Oren <noren@nvidia.com>
+Link: https://patch.msgid.link/20260520153928.3371765-1-noren@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../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
+
--- /dev/null
+From cdabb265efcef19de5f4f34e989dd630fca225ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 18:19:40 +0800
+Subject: selftests: ublk: cap nthreads to kernel's actual nr_hw_queues
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+[ 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 <tom.leiming@gmail.com>
+Link: https://patch.msgid.link/20260513101941.1373998-1-tom.leiming@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
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
--- /dev/null
+From e86e68f66b10d2d289ad1b027495014e370614f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <ustc.gu@gmail.com>
+
+[ 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 <ustc.gu@gmail.com>
+Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From e0a31860c72016fe059edeabf67c1c210c48cd14 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 19:54:41 +0200
+Subject: srcu: Don't queue workqueue handlers to never-online CPUs
+
+From: Paul E. McKenney <paulmck@kernel.org>
+
+[ 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 <gor@linux.ibm.com>
+Fixes: 61bbcfb50514 ("srcu: Push srcu_node allocation to GP when non-preemptible")
+Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Samir <samir@linux.ibm.com>
+Reviewed-by: Shrikanth Hegde <sshegde@linux.ibm.com>
+Cc: Tejun Heo <tj@kernel.org>
+Signed-off-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
+Signed-off-by: Boqun Feng <boqun@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 4f139f42f64488b2e0b4b7a1ec07f2d3dfbdf89c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 00:57:38 -0700
+Subject: tap: fix stack info leak in tap_ioctl() SIOCGIFHWADDR
+
+From: Weiming Shi <bestswngs@gmail.com>
+
+[ 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 <xmei5@asu.edu>
+Signed-off-by: Weiming Shi <bestswngs@gmail.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20260520075736.3415676-3-bestswngs@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9983c80a05b28bcdb8fad91cd38d42e1722e29c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 03:59:19 +0000
+Subject: tcp: Fix imbalanced icsk_accept_queue count.
+
+From: Kuniyuki Iwashima <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ee682d49eaae43bccbf9542fbcbcc59006e85554 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <kuniyu@google.com>
+
+[ 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 <melotti@google.com>
+Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 961b8e393c482f4a0dbc5eabf25ec5d39faa3e9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <edumazet@google.com>
+
+[ 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 <clm@meta.com>
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260519084611.2485277-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 3eb9b88e65f1f5e1508ed7c1c9fe85cbd12bf72d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:56:36 +0900
+Subject: test_kprobes: clear kprobes between test runs
+
+From: Martin Kaiser <martin@kaiser.cx>
+
+[ 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 <martin@kaiser.cx>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ae4f3179fc5e792c8daeb28bf5282cbc8f6b0f05 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 May 2026 08:58:25 -0400
+Subject: tls: Preserve sk_err across recvmsg() when data has been copied
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ 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 <kuba@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 88d75e8be0907571c97f335d12156e4424029e1f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 20:57:47 +0100
+Subject: tracing: Avoid NULL return from hist_field_name() on truncation
+
+From: David Carlier <devnexen@gmail.com>
+
+[ 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 <devnexen@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 21afc1133f8c173b1c0531aed215d52512e201b1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 May 2026 22:48:43 +0800
+Subject: ublk: reject max_sectors smaller than PAGE_SECTORS in parameter
+ validation
+
+From: Ming Lei <tom.leiming@gmail.com>
+
+[ 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 <tom.leiming@gmail.com>
+Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9d3019d783370b953a87166703348daf8f70a3dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 09:22:50 +0300
+Subject: udp: Fix UDP length on last GSO_PARTIAL segment
+
+From: Gal Pressman <gal@nvidia.com>
+
+[ 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 <matthew.schwartz@linux.dev>
+Closes: https://lore.kernel.org/all/6c3fb15e-711d-4b8d-b152-e03d9b05293f@linux.dev/
+Tested-by: Matthew Schwartz <matthew.schwartz@linux.dev>
+Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
+Signed-off-by: Gal Pressman <gal@nvidia.com>
+Link: https://patch.msgid.link/20260518062250.3019914-3-gal@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From dad8a316e29dbdadc9a5aed13c913139fc5a8fc1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 May 2026 09:22:49 +0300
+Subject: udp: gso: Fix handling checksum in __udp_gso_segment
+
+From: Alice Mikityanska <alice@isovalent.com>
+
+[ 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 <alice@isovalent.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Gal Pressman <gal@nvidia.com>
+Link: https://patch.msgid.link/20260518062250.3019914-2-gal@nvidia.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 344f56433b7974199ef59af0564ace811d6b40f6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 07:58:24 -0700
+Subject: vfio/pci: Check BAR resources before exporting a DMABUF
+
+From: Matt Evans <mattev@meta.com>
+
+[ 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 <mattev@meta.com>
+Link: https://lore.kernel.org/r/20260511145829.2993601-3-mattev@meta.com
+Signed-off-by: Alex Williamson <alex@shazbot.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d44580e4dadb4a8acc05cde0eb2ce0238b63059d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 May 2026 11:29:48 +0200
+Subject: vsock/virtio: fix zerocopy completion for multi-skb sends
+
+From: Stefano Garzarella <sgarzare@redhat.com>
+
+[ 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 <maherazz04@gmail.com>
+Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
+Acked-by: Michael S. Tsirkin <mst@redhat.com>
+Acked-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
+Link: https://patch.msgid.link/20260514092948.268720-1-sgarzare@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 74035002d62d2d9bd519b15a833d0593f48dcc55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Apr 2026 14:17:37 +0800
+Subject: wifi: ath10k: skip WMI and beacon transmission when device is wedged
+
+From: Kang Yang <kang.yang@oss.qualcomm.com>
+
+[ 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 <kang.yang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 9ae5293a4c622e72817c2a0e0eaf37b3fa1ec7f0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fa5ca08f49a325180906aee420b9f8f4a2097dd6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 May 2026 15:42:38 +0200
+Subject: wifi: ath11k: fix error path leaks in some WMI WOW calls
+
+From: Nicolas Escande <nico.escande@gmail.com>
+
+[ 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 <nico.escande@gmail.com>
+Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 12857262b62c11b4d0331c92b29e5fbdb365e64e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <matthew.leach@collabora.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
+Reviewed-by: P Praneesh <praneesh.p@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From bfd948eb53854164a35d83cb7f1065fd40904f76 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <baochen.qiang@oss.qualcomm.com>
+
+[ 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 <baochen.qiang@oss.qualcomm.com>
+Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260514-ath12k-fix-20mhz-only-mcs-map-v1-1-a38d4a9b21a2@oss.qualcomm.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From ef6fa40f324cca13dc20e871ef2f7371c6e0a62e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+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 <miriam.rachel.korenblit@intel.com>
+
+[ 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 <johannes.berg@intel.com>
+Link: https://patch.msgid.link/20260515151351.371f40fc6711.I6a82cfe9655564e9c5731af91c36493b26b1208e@changeid
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 90c6a276f69b176c08a7d1f8ee5399fa6d2b7c04 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 4 Apr 2026 22:41:44 -0700
+Subject: wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is
+ disabled
+
+From: Cole Leavitt <cole@unwrap.rs>
+
+[ 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 <miriam.rachel.korenblit@intel.com>
+Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
+Signed-off-by: Cole Leavitt <cole@unwrap.rs>
+Link: https://patch.msgid.link/20260405054145.1064152-3-cole@unwrap.rs
+Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From f68ee43e14c4936aaf1379970066b0cd0efbeb35 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 May 2026 12:29:08 +0200
+Subject: wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs
+
+From: Alexandru Hossu <hossu.alexandru@gmail.com>
+
+[ 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 <hossu.alexandru@gmail.com>
+Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From cd2d33e57921f1d74e534dd6e6c1a7d33eeeefcb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:10:31 +0200
+Subject: wifi: mac80211: fix MLE defragmentation
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ 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 <miriam.rachel.korenblit@intel.com>
+Reviewed-by: Ilan Peer <ilan.peer@intel.com>
+Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 700912ba9e9a58141bac3a7d111d7140bcb52342 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 May 2026 09:10:32 +0200
+Subject: wifi: mac80211: fix multi-link element inheritance
+
+From: Johannes Berg <johannes.berg@intel.com>
+
+[ 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 <ilan.peer@intel.com>
+Reviewed-by: Benjamin Berg <benjamin.berg@intel.com>
+Link: https://patch.msgid.link/20260508091032.92184c0a3f08.I3c43b0b63d2cef8a4ddddaef1c2faaeb1de711ad@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 12284f270d799614e54b15fba220b8b783cbe743 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 11 May 2026 09:57:32 +0530
+Subject: wifi: wilc1000: fix dma_buffer leak on bus acquire failure
+
+From: Shitalkumar Gandhi <shital.gandhi45@gmail.com>
+
+[ 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 <shitalkumar.gandhi@cambiumnetworks.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260511042732.998311-1-shitalkumar.gandhi@cambiumnetworks.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From d51eab7abd3bb24bafdc61c131557f44df434866 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 16 Mar 2026 16:12:00 +0100
+Subject: x86/mce: Restore MCA polling interval halving
+
+From: Borislav Petkov (AMD) <bp@alien8.de>
+
+[ 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 <lirongqing@baidu.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Reviewed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+Tested-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+Link: https://lore.kernel.org/r/20260112082747.2842-1-lirongqing@baidu.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From fdb7567d368b87a0877d1d5c19addd968dbf0cd1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 May 2026 12:24:17 +0200
+Subject: x86/xen: Fix xen_e820_swap_entry_with_ram()
+
+From: Juergen Gross <jgross@suse.com>
+
+[ 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 <jbeulich@suse.com>
+Reviewed-by: Jan Beulich <jbeulich@suse.com>
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Message-ID: <20260505102417.208138-1-jgross@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
--- /dev/null
+From 5209ed8a3464d8382a9e46c070be297c4926b3c3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Apr 2026 22:58:15 +0200
+Subject: zonefs: handle integer overflow in zonefs_fname_to_fno
+
+From: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+
+[ 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 <adobriyan@gmail.com>
+Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed")
+Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+