From fc1e8a6f129d87c64ac8e58b50d9dfa66217cfda Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 18 Feb 2026 14:21:35 -0800 Subject: [PATCH 001/700] Input: bcm5974 - recover from failed mode switch Mode switches sent before control response are ignored. This results in an unresponsive trackpad and "bcm5974: bad trackpad package, length: 8" repeated in logs. On receiving unknown 8-byte packets, assume that mode switch was ignored and schedule an asynchronous mode reset. The reset will switch the device to normal mode, wait, then switch back to wellspring mode. Signed-off-by: Liam Mitchell Link: https://lore.kernel.org/linux-input/CAOQ1CL4+DP1TuLAGNsz5GdFBTHvnTg=5q=Dr2Z1OQc6RXydSYA@mail.gmail.com/ Acked-by: Henrik Rydberg Link: https://patch.msgid.link/20260213-bcm5974-reset-v2-1-1837851336b0@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/bcm5974.c | 42 ++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index dfdfb59cc8b597..fe52f15c0c10dd 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -286,6 +286,8 @@ struct bcm5974 { const struct tp_finger *index[MAX_FINGERS]; /* finger index data */ struct input_mt_pos pos[MAX_FINGERS]; /* position array */ int slots[MAX_FINGERS]; /* slot assignments */ + struct work_struct mode_reset_work; + unsigned long last_mode_reset; }; /* trackpad finger block data, le16-aligned */ @@ -696,6 +698,32 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) return retval; } +/* + * Mode switches sent before the control response are ignored. + * Fixing this state requires switching to normal mode and waiting + * about 1ms before switching back to wellspring mode. + */ +static void bcm5974_mode_reset_work(struct work_struct *work) +{ + struct bcm5974 *dev = container_of(work, struct bcm5974, mode_reset_work); + int error; + + guard(mutex)(&dev->pm_mutex); + dev->last_mode_reset = jiffies; + + error = bcm5974_wellspring_mode(dev, false); + if (error) { + dev_err(&dev->intf->dev, "reset to normal mode failed\n"); + return; + } + + fsleep(1000); + + error = bcm5974_wellspring_mode(dev, true); + if (error) + dev_err(&dev->intf->dev, "mode switch after reset failed\n"); +} + static void bcm5974_irq_button(struct urb *urb) { struct bcm5974 *dev = urb->context; @@ -752,10 +780,20 @@ static void bcm5974_irq_trackpad(struct urb *urb) if (dev->tp_urb->actual_length == 2) goto exit; - if (report_tp_state(dev, dev->tp_urb->actual_length)) + if (report_tp_state(dev, dev->tp_urb->actual_length)) { dprintk(1, "bcm5974: bad trackpad package, length: %d\n", dev->tp_urb->actual_length); + /* + * Receiving a HID packet means we aren't in wellspring mode. + * If we haven't tried a reset in the last second, try now. + */ + if (dev->tp_urb->actual_length == 8 && + time_after(jiffies, dev->last_mode_reset + msecs_to_jiffies(1000))) { + schedule_work(&dev->mode_reset_work); + } + } + exit: error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); if (error) @@ -906,6 +944,7 @@ static int bcm5974_probe(struct usb_interface *iface, dev->intf = iface; dev->input = input_dev; dev->cfg = *cfg; + INIT_WORK(&dev->mode_reset_work, bcm5974_mode_reset_work); mutex_init(&dev->pm_mutex); /* setup urbs */ @@ -998,6 +1037,7 @@ static void bcm5974_disconnect(struct usb_interface *iface) { struct bcm5974 *dev = usb_get_intfdata(iface); + disable_work_sync(&dev->mode_reset_work); usb_set_intfdata(iface, NULL); input_unregister_device(dev->input); From 29d1f56c4f3001b7f547123e0a307c009ac717f8 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 10 Feb 2026 13:01:42 +0100 Subject: [PATCH 002/700] Revert "arm64: dts: rockchip: Further describe the WiFi for the Pinebook Pro" This reverts commit 6d54d935062e2d4a7d3f779ceb9eeff108d0535d. It seems there are different variants of the Wifi chipset in use on the Pinebook Pro. And according to the reported regression - see Closes below, the reverted change causes issues with one Wifi chipset. The original commit message indicates a "further description" only and does not indicate this would fix an actual problem, so a revert should not cause further problems. Fixes: 6d54d935062e ("arm64: dts: rockchip: Further describe the WiFi for the Pinebook Pro") Cc: Jan Palus Cc: Peter Robinson Cc: Thorsten Leemhuis Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/r/aUKOlj-RvTYlrpiS@rock.grzadka/ Tested-by: Jan Palus Reviewed-by: Dragan Simic Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260210120142.698512-1-heiko@sntech.de Signed-off-by: Heiko Stuebner --- .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts index 753d513449540f..ae937a3afa1136 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts @@ -879,12 +879,6 @@ }; }; - wifi { - wifi_host_wake_l: wifi-host-wake-l { - rockchip,pins = <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - wireless-bluetooth { bt_wake_pin: bt-wake-pin { rockchip,pins = <2 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; @@ -942,19 +936,7 @@ pinctrl-names = "default"; pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; sd-uhs-sdr104; - #address-cells = <1>; - #size-cells = <0>; status = "okay"; - - brcmf: wifi@1 { - compatible = "brcm,bcm4329-fmac"; - reg = <1>; - interrupt-parent = <&gpio0>; - interrupts = ; - interrupt-names = "host-wake"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_l>; - }; }; &sdhci { From 7ec1bd3d9be671d04325b9e06149b8813f6a4836 Mon Sep 17 00:00:00 2001 From: Potin Lai Date: Thu, 22 Jan 2026 16:37:56 +0800 Subject: [PATCH 003/700] soc: aspeed: socinfo: Mask table entries for accurate SoC ID matching The siliconid_to_name() function currently masks the input silicon ID with 0xff00ffff, but compares it against unmasked table entries. This causes matching to fail if the table entries contain non-zero values in the bits covered by the mask (bits 16-23). Update the logic to apply the 0xff00ffff mask to the table entries during comparison. This ensures that only the relevant model and revision bits are considered, providing a consistent match across different manufacturing batches. [arj: Add Fixes: tag, fix 'soninfo' typo, clarify function reference] Fixes: e0218dca5787 ("soc: aspeed: Add soc info driver") Signed-off-by: Potin Lai Link: https://patch.msgid.link/20260122-soc_aspeed_name_fix-v1-1-33a847f2581c@gmail.com Signed-off-by: Andrew Jeffery --- drivers/soc/aspeed/aspeed-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c index 5e34e01ad26de6..fb8fde94b6513b 100644 --- a/drivers/soc/aspeed/aspeed-socinfo.c +++ b/drivers/soc/aspeed/aspeed-socinfo.c @@ -39,7 +39,7 @@ static const char *siliconid_to_name(u32 siliconid) unsigned int i; for (i = 0 ; i < ARRAY_SIZE(rev_table) ; ++i) { - if (rev_table[i].id == id) + if ((rev_table[i].id & 0xff00ffff) == id) return rev_table[i].name; } From 3ecea84d2b90bbf934d5ca75514fa902fd71e03f Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Fri, 20 Feb 2026 20:44:58 +0800 Subject: [PATCH 004/700] arm64: dts: qcom: hamoa/x1: fix idle exit latency Designs based on the Qualcomm X1 Hamoa reference platform report: driver: Idle state 1 target residency too low This is because the declared X1 idle entry plus exit latency of 680us exceeds the declared minimum 600us residency time: entry-latency-us = <180>; exit-latency-us = <500>; min-residency-us = <600>; Fix this to be 320us so the sum of the entry and exit latencies matches the downstream 500us exit latency, as directed by Maulik. Tested on a Lenovo Yoga Slim 7x with Qualcomm X1E-80-100. Fixes: 2e65616ef07f ("arm64: dts: qcom: x1e80100: Update C4/C5 residency/exit numbers") Signed-off-by: Daniel J Blueman Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260220124626.8611-1-daniel@quora.org Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/hamoa.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi index db65c392e61899..4b0784af4bd395 100644 --- a/arch/arm64/boot/dts/qcom/hamoa.dtsi +++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi @@ -269,7 +269,7 @@ idle-state-name = "ret"; arm,psci-suspend-param = <0x00000004>; entry-latency-us = <180>; - exit-latency-us = <500>; + exit-latency-us = <320>; min-residency-us = <600>; }; }; From b7df21c59739cceb7b866c6c5e8a6ba03875ab71 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 20 Feb 2026 14:32:20 +0530 Subject: [PATCH 005/700] arm64: dts: qcom: qcm6490-idp: Fix WCD9370 reset GPIO polarity The WCD9370 audio codec reset line on QCM6490 IDP should be active-low, but the device tree described it as active-high. As a result, the codec is kept in reset and fails to reset the SoundWire, leading to timeouts and ASoC card probe failure (-ETIMEDOUT). Fix the reset GPIO polarity to GPIO_ACTIVE_LOW so the codec can properly initialize. Fixes: aa04c298619f ("arm64: dts: qcom: qcm6490-idp: Add WSA8830 speakers and WCD9370 headset codec") Signed-off-by: Ravi Hothi Reviewed-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260220090220.2992193-1-ravi.hothi@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/qcm6490-idp.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/qcm6490-idp.dts b/arch/arm64/boot/dts/qcom/qcm6490-idp.dts index 089a027c57d5ca..b2f00e107643dc 100644 --- a/arch/arm64/boot/dts/qcom/qcm6490-idp.dts +++ b/arch/arm64/boot/dts/qcom/qcm6490-idp.dts @@ -177,7 +177,7 @@ pinctrl-0 = <&wcd_default>; pinctrl-names = "default"; - reset-gpios = <&tlmm 83 GPIO_ACTIVE_HIGH>; + reset-gpios = <&tlmm 83 GPIO_ACTIVE_LOW>; vdd-buck-supply = <&vreg_l17b_1p7>; vdd-rxtx-supply = <&vreg_l18b_1p8>; From 5839419cffc7788a356428d321e3ec18055c0286 Mon Sep 17 00:00:00 2001 From: Christoffer Sandberg Date: Mon, 23 Feb 2026 15:20:45 +0100 Subject: [PATCH 006/700] Input: i8042 - add TUXEDO InfinityBook Max 16 Gen10 AMD to i8042 quirk table The device occasionally wakes up from suspend with missing input on the internal keyboard and the following suspend attempt results in an instant wake-up. The quirks fix both issues for this device. Signed-off-by: Christoffer Sandberg Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260223142054.50310-1-wse@tuxedocomputers.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-acpipnpio.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index d2cf940b105a69..8ebdf4fb903080 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -1187,6 +1187,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"), From 7adaaee5edd35a423ae199c41b86bd1ed60ed483 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 23 Feb 2026 15:05:15 -0800 Subject: [PATCH 007/700] Input: synaptics-rmi4 - fix a locking bug in an error path Lock f54->data_mutex when entering the function statement since jumping to the 'error' label when checking report_size fails causes that mutex to be unlocked. This bug has been detected by the Clang thread-safety checker. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/20260223215118.2154194-16-bvanassche@acm.org Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index ac4041a69fcd3a..61909e1a39e248 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -538,6 +538,8 @@ static void rmi_f54_work(struct work_struct *work) int error; int i; + mutex_lock(&f54->data_mutex); + report_size = rmi_f54_get_report_size(f54); if (report_size == 0) { dev_err(&fn->dev, "Bad report size, report type=%d\n", @@ -546,8 +548,6 @@ static void rmi_f54_work(struct work_struct *work) goto error; /* retry won't help */ } - mutex_lock(&f54->data_mutex); - /* * Need to check if command has completed. * If not try again later. From 1f99b5d93d99ca17d50b386a674d0ce1f20932d8 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Wed, 28 Jan 2026 00:28:28 +0100 Subject: [PATCH 008/700] arm64: dts: imx8mq: Set the correct gpu_ahb clock frequency According to i.MX 8M Quad Reference Manual, GPU_AHB_CLK_ROOT's maximum frequency is 400MHz. Fixes: 45d2c84eb3a2 ("arm64: dts: imx8mq: add GPU node") Reviewed-by: Frank Li Signed-off-by: Sebastian Krzyszkowiak Reviewed-by: Peng Fan Reviewed-by: Fabio Estevam Signed-off-by: Frank Li --- arch/arm64/boot/dts/freescale/imx8mq.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 607962f807bebe..6a25e219832ced 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -1632,7 +1632,7 @@ <&clk IMX8MQ_GPU_PLL_OUT>, <&clk IMX8MQ_GPU_PLL>; assigned-clock-rates = <800000000>, <800000000>, - <800000000>, <800000000>, <0>; + <800000000>, <400000000>, <0>; power-domains = <&pgc_gpu>; }; From 08903184553def7ba1ad6ba4fa8afe1ba2ee0a21 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Tue, 3 Feb 2026 19:23:08 +0800 Subject: [PATCH 009/700] arm64: dts: imx93-9x9-qsb: change usdhc tuning step for eMMC and SD During system resume, the following errors occurred: [ 430.638625] mmc1: error -84 writing Cache Enable bit [ 430.643618] mmc1: error -84 doing runtime resume For eMMC and SD, there are two tuning pass windows and the gap between those two windows may only have one cell. If tuning step > 1, the gap may just be skipped and host assumes those two windows as a continuous windows. This will cause a wrong delay cell near the gap to be selected. Set the tuning step to 1 to avoid selecting the wrong delay cell. For SDIO, the gap is sufficiently large, so the default tuning step does not cause this issue. Fixes: 0565d20cd8c2 ("arm64: dts: freescale: Support i.MX93 9x9 Quick Start Board") Signed-off-by: Luke Wang Reviewed-by: Frank Li Signed-off-by: Frank Li --- arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts index 0852067eab2cb8..197c8f8b7f6696 100644 --- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts @@ -507,6 +507,7 @@ pinctrl-2 = <&pinctrl_usdhc1_200mhz>; bus-width = <8>; non-removable; + fsl,tuning-step = <1>; status = "okay"; }; @@ -519,6 +520,7 @@ vmmc-supply = <®_usdhc2_vmmc>; bus-width = <4>; no-mmc; + fsl,tuning-step = <1>; status = "okay"; }; From 44db7bc66eb38e85bb32777c5fd3a4e7baa84147 Mon Sep 17 00:00:00 2001 From: Markus Niebel Date: Mon, 9 Feb 2026 16:50:13 +0100 Subject: [PATCH 010/700] arm64: dts: imx91-tqma9131: improve eMMC pad configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use DSE x4 an PullUp for CMD an DAT, DSE x4 and PullDown for CLK to improve stability and detection at low temperatures under -25°C. Fixes: e71db39f0c7c ("arm64: dts: freescale: add initial device tree for TQMa91xx/MBa91xxCA") Signed-off-by: Markus Niebel Signed-off-by: Alexander Stein Reviewed-by: Frank Li Signed-off-by: Frank Li --- .../boot/dts/freescale/imx91-tqma9131.dtsi | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx91-tqma9131.dtsi b/arch/arm64/boot/dts/freescale/imx91-tqma9131.dtsi index 5792952b7a8e14..c99d7bc1684836 100644 --- a/arch/arm64/boot/dts/freescale/imx91-tqma9131.dtsi +++ b/arch/arm64/boot/dts/freescale/imx91-tqma9131.dtsi @@ -272,20 +272,20 @@ /* enable SION for data and cmd pad due to ERR052021 */ pinctrl_usdhc1: usdhc1grp { fsl,pins = /* PD | FSEL 3 | DSE X5 */ - , + , /* HYS | FSEL 0 | no drive */ , /* HYS | FSEL 3 | X5 */ - , + , /* HYS | FSEL 3 | X4 */ - , - , - , - , - , - , - , - ; + , + , + , + , + , + , + , + ; }; pinctrl_wdog: wdoggrp { From b6c94c71f349479b76fcc0ef0dc7147f3f326dff Mon Sep 17 00:00:00 2001 From: Markus Niebel Date: Mon, 9 Feb 2026 16:50:14 +0100 Subject: [PATCH 011/700] arm64: dts: imx93-tqma9352: improve eMMC pad configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use DSE x4 an PullUp for CMD an DAT, DSE x4 and PullDown for CLK to improve stability and detection at low temperatures under -25°C. Fixes: 0b5fdfaa8e45 ("arm64: dts: freescale: imx93-tqma9352: set SION for cmd and data pad of USDHC") Signed-off-by: Markus Niebel Signed-off-by: Alexander Stein Reviewed-by: Frank Li Signed-off-by: Frank Li --- .../boot/dts/freescale/imx93-tqma9352.dtsi | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi index 3a23e2eb9febe8..ce34a296495c45 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi @@ -271,21 +271,21 @@ /* enable SION for data and cmd pad due to ERR052021 */ pinctrl_usdhc1: usdhc1grp { fsl,pins = < - /* PD | FSEL 3 | DSE X5 */ - MX93_PAD_SD1_CLK__USDHC1_CLK 0x5be + /* PD | FSEL 3 | DSE X4 */ + MX93_PAD_SD1_CLK__USDHC1_CLK 0x59e /* HYS | FSEL 0 | no drive */ MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x1000 - /* HYS | FSEL 3 | X5 */ - MX93_PAD_SD1_CMD__USDHC1_CMD 0x400011be - /* HYS | FSEL 3 | X4 */ - MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x4000119e - MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x4000119e - MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x4000119e - MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x4000119e - MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x4000119e - MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x4000119e - MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x4000119e - MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x4000119e + /* HYS | PU | FSEL 3 | DSE X4 */ + MX93_PAD_SD1_CMD__USDHC1_CMD 0x4000139e + /* HYS | PU | FSEL 3 | DSE X4 */ + MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x4000139e + MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x4000139e + MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x4000139e + MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x4000139e + MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x4000139e + MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x4000139e + MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x4000139e + MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x4000139e >; }; From 16fdabe143fce2cbf89139677728e17e21b46c28 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Thu, 5 Feb 2026 09:28:40 +0000 Subject: [PATCH 012/700] gpio: Fix resource leaks on errors in gpiochip_add_data_with_key() Since commit aab5c6f20023 ("gpio: set device type for GPIO chips"), `gdev->dev.release` is unset. As a result, the reference count to `gdev->dev` isn't dropped on the error handling paths. Drop the reference on errors. Also reorder the instructions to make the error handling simpler. Now gpiochip_add_data_with_key() roughly looks like: >>> Some memory allocation. Go to ERR ZONE 1 on errors. >>> device_initialize(). gpiodev_release() takes over the responsibility for freeing the resources of `gdev->dev`. The subsequent error handling paths shouldn't go through ERR ZONE 1 again which leads to double free. >>> Some initialization mainly on `gdev`. >>> The rest of initialization. Go to ERR ZONE 2 on errors. >>> Chip registration success and exit. >>> ERR ZONE 2. gpio_device_put() and exit. >>> ERR ZONE 1. Cc: stable@vger.kernel.org Fixes: aab5c6f20023 ("gpio: set device type for GPIO chips") Reviewed-by: Linus Walleij Signed-off-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260205092840.2574840-1-tzungbi@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 101 ++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 86a171e96b0ebc..f77d5121a8a87d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -892,13 +892,15 @@ static const struct device_type gpio_dev_type = { #define gcdev_unregister(gdev) device_del(&(gdev)->dev) #endif +/* + * An initial reference count has been held in gpiochip_add_data_with_key(). + * The caller should drop the reference via gpio_device_put() on errors. + */ static int gpiochip_setup_dev(struct gpio_device *gdev) { struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev); int ret; - device_initialize(&gdev->dev); - /* * If fwnode doesn't belong to another device, it's safe to clear its * initialized flag. @@ -964,9 +966,11 @@ static void gpiochip_setup_devs(void) list_for_each_entry_srcu(gdev, &gpio_devices, list, srcu_read_lock_held(&gpio_devices_srcu)) { ret = gpiochip_setup_dev(gdev); - if (ret) + if (ret) { + gpio_device_put(gdev); dev_err(&gdev->dev, "Failed to initialize gpio device (%d)\n", ret); + } } } @@ -1047,71 +1051,72 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret; - /* - * First: allocate and populate the internal stat container, and - * set up the struct device. - */ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); if (!gdev) return -ENOMEM; - - gdev->dev.type = &gpio_dev_type; - gdev->dev.bus = &gpio_bus_type; - gdev->dev.parent = gc->parent; - rcu_assign_pointer(gdev->chip, gc); - gc->gpiodev = gdev; gpiochip_set_data(gc, data); - device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); - ret = ida_alloc(&gpio_ida, GFP_KERNEL); if (ret < 0) goto err_free_gdev; gdev->id = ret; - ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); + ret = init_srcu_struct(&gdev->srcu); if (ret) goto err_free_ida; + rcu_assign_pointer(gdev->chip, gc); - if (gc->parent && gc->parent->driver) - gdev->owner = gc->parent->driver->owner; - else if (gc->owner) - /* TODO: remove chip->owner */ - gdev->owner = gc->owner; - else - gdev->owner = THIS_MODULE; + ret = init_srcu_struct(&gdev->desc_srcu); + if (ret) + goto err_cleanup_gdev_srcu; + + ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); + if (ret) + goto err_cleanup_desc_srcu; + + device_initialize(&gdev->dev); + /* + * After this point any allocated resources to `gdev` will be + * free():ed by gpiodev_release(). If you add new resources + * then make sure they get free():ed there. + */ + gdev->dev.type = &gpio_dev_type; + gdev->dev.bus = &gpio_bus_type; + gdev->dev.parent = gc->parent; + device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); ret = gpiochip_get_ngpios(gc, &gdev->dev); if (ret) - goto err_free_dev_name; + goto err_put_device; + gdev->ngpio = gc->ngpio; gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); if (!gdev->descs) { ret = -ENOMEM; - goto err_free_dev_name; + goto err_put_device; } gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { ret = -ENOMEM; - goto err_free_descs; + goto err_put_device; } - gdev->ngpio = gc->ngpio; gdev->can_sleep = gc->can_sleep; - rwlock_init(&gdev->line_state_lock); RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); - - ret = init_srcu_struct(&gdev->srcu); - if (ret) - goto err_free_label; - - ret = init_srcu_struct(&gdev->desc_srcu); - if (ret) - goto err_cleanup_gdev_srcu; +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&gdev->pin_ranges); +#endif + if (gc->parent && gc->parent->driver) + gdev->owner = gc->parent->driver->owner; + else if (gc->owner) + /* TODO: remove chip->owner */ + gdev->owner = gc->owner; + else + gdev->owner = THIS_MODULE; scoped_guard(mutex, &gpio_devices_lock) { /* @@ -1127,7 +1132,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (base < 0) { ret = base; base = 0; - goto err_cleanup_desc_srcu; + goto err_put_device; } /* @@ -1147,14 +1152,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiodev_add_to_list_unlocked(gdev); if (ret) { gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_cleanup_desc_srcu; + goto err_put_device; } } -#ifdef CONFIG_PINCTRL - INIT_LIST_HEAD(&gdev->pin_ranges); -#endif - if (gc->names) gpiochip_set_desc_names(gc); @@ -1248,25 +1249,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, scoped_guard(mutex, &gpio_devices_lock) list_del_rcu(&gdev->list); synchronize_srcu(&gpio_devices_srcu); - if (gdev->dev.release) { - /* release() has been registered by gpiochip_setup_dev() */ - gpio_device_put(gdev); - goto err_print_message; - } +err_put_device: + gpio_device_put(gdev); + goto err_print_message; + err_cleanup_desc_srcu: cleanup_srcu_struct(&gdev->desc_srcu); err_cleanup_gdev_srcu: cleanup_srcu_struct(&gdev->srcu); -err_free_label: - kfree_const(gdev->label); -err_free_descs: - kfree(gdev->descs); -err_free_dev_name: - kfree(dev_name(&gdev->dev)); err_free_ida: ida_free(&gpio_ida, gdev->id); err_free_gdev: kfree(gdev); + err_print_message: /* failures here can mean systems won't boot... */ if (ret != -EPROBE_DEFER) { From 48a5c36577ebe0144f8ede70e59b59ea18b75089 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 28 Feb 2026 01:48:19 +0800 Subject: [PATCH 013/700] iio: adc: ti-ads1119: Fix unbalanced pm reference count in ds1119_single_conversion() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ads1119_single_conversion(), if pm_runtime_resume_and_get() fails, the code jumps to the pdown label, which calls pm_runtime_put_autosuspend(). Since pm_runtime_resume_and_get() automatically decrements the usage counter on failure, the subsequent call to pm_runtime_put_autosuspend() causes an unbalanced reference counter. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: João Paulo Gonçalves Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index c9cedc59cdcdb1..4454f28b2b58e3 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -274,7 +274,7 @@ static int ads1119_single_conversion(struct ads1119_state *st, ret = pm_runtime_resume_and_get(dev); if (ret) - goto pdown; + return ret; ret = ads1119_configure_channel(st, mux, gain, datarate); if (ret) From c53bca092486809d266b5921b9e6f9df2688fc26 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 27 Feb 2026 21:54:29 +0800 Subject: [PATCH 014/700] iio: pressure: abp2030pa: Remove IRQF_ONESHOT from devm_request_irq() Since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Remove IRQF_ONESHOT from devm_request_irq(). Fixes: 47d323ce1e89 ("iio: pressure: add Honeywell ABP2 driver") Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Felix Gu Tested-by: Petre Rodan Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/abp2030pa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c index 4ca056a73cefce..b44f1bf4c633fe 100644 --- a/drivers/iio/pressure/abp2030pa.c +++ b/drivers/iio/pressure/abp2030pa.c @@ -520,7 +520,7 @@ int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq) data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin; if (data->irq > 0) { - ret = devm_request_irq(dev, irq, abp2_eoc_handler, IRQF_ONESHOT, + ret = devm_request_irq(dev, irq, abp2_eoc_handler, 0, dev_name(dev), data); if (ret) return ret; From 0206dd36418c104c0b3dea4ed7047e21eccb30b0 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 15:33:30 +0200 Subject: [PATCH 015/700] iio: adc: ade9000: move mutex init before IRQ registration Move devm_mutex_init() before ade9000_request_irq() calls so that st->lock is initialized before any handler that depends on it can run. Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index db085dc5e52661..c62c6480dd0a90 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1706,19 +1706,19 @@ static int ade9000_probe(struct spi_device *spi) init_completion(&st->reset_completion); - ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); + ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; - ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev); + ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); if (ret) return ret; - ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev); + ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev); if (ret) return ret; - ret = devm_mutex_init(dev, &st->lock); + ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev); if (ret) return ret; From bd66aa1c8b8cabf459064a46d3430a5ec5138418 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 14:43:05 +0200 Subject: [PATCH 016/700] iio: accel: adxl380: fix FIFO watermark bit 8 always written as 0 FIELD_PREP(BIT(0), fifo_samples & BIT(8)) produces either 0 or 256, and since FIELD_PREP masks to bit 0, 256 & 1 evaluates to 0. Use !! to convert the result to a proper 0-or-1 value. Fixes: df36de13677a ("iio: accel: add ADXL380 driver") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl380.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index 8fab2fdbe147e2..a51d1d61c41275 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -877,7 +877,7 @@ static int adxl380_set_fifo_samples(struct adxl380_state *st) ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG, ADXL380_FIFO_SAMPLES_8_MSK, FIELD_PREP(ADXL380_FIFO_SAMPLES_8_MSK, - (fifo_samples & BIT(8)))); + !!(fifo_samples & BIT(8)))); if (ret) return ret; From 86133fb1ec36b2f5cec29d71fbae84877c3a1358 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Thu, 26 Feb 2026 18:07:02 +0400 Subject: [PATCH 017/700] iio: adc: ade9000: fix wrong register in CALIBBIAS case for active power The switch statement in ade9000_write_raw() attempts to match chan->address against ADE9000_REG_AWATTOS (0x00F) to dispatch the calibration offset write for active power channels. However, chan->address is set via ADE9000_ADDR_ADJUST(ADE9000_REG_AWATT, num), so after masking the phase bits, tmp holds ADE9000_REG_AWATT (0x210), which never matches 0x00F. As a result, writing IIO_CHAN_INFO_CALIBBIAS for IIO_POWER always falls through to the default case and returns -EINVAL, making active power offset calibration silently broken. Fix this by matching against ADE9000_REG_AWATT instead, which is the actual base address stored in chan->address for watt channels. Reference:ADE9000 datasheet (Rev. B), AWATTOS is the offset correction register at 0x00F (p. 44), while AWATT is the total active power register at 0x210 (p. 48). Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c62c6480dd0a90..945a159e5de66b 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -1123,7 +1123,7 @@ static int ade9000_write_raw(struct iio_dev *indio_dev, tmp &= ~ADE9000_PHASE_C_POS_BIT; switch (tmp) { - case ADE9000_REG_AWATTOS: + case ADE9000_REG_AWATT: return regmap_write(st->regmap, ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS, chan->channel), val); From 57b207e38d414a27fda9fff638a0d3e7ef16b917 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Tue, 24 Feb 2026 17:23:55 +0400 Subject: [PATCH 018/700] iio: adc: ade9000: fix wrong return type in streaming push The else branch of ade9000_iio_push_streaming() incorrectly returns IRQ_HANDLED on regmap_write failure. This function returns int (0 on success, negative errno on failure), so IRQ_HANDLED (1) would be misinterpreted as a non-error by callers. Return ret instead, consistent with every other error path in the function. Fixes: 81de7b4619fc ("iio: adc: add ade9000 support") Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 945a159e5de66b..1abbfdfcd554c8 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -787,7 +787,7 @@ static int ade9000_iio_push_streaming(struct iio_dev *indio_dev) ADE9000_MIDDLE_PAGE_BIT); if (ret) { dev_err_ratelimited(dev, "IRQ0 WFB write fail"); - return IRQ_HANDLED; + return ret; } ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF); From b403981da8a26f57f07859a9704392b1183e2a6e Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 25 Feb 2026 22:48:57 +0800 Subject: [PATCH 019/700] iio: dac: mcp47feb02: Fix mutex used before initialization The mcp47feb02_parse_fw() function uses data->lock, but the mutex was initialized after this function in probe path. Since mcp47feb02_parse_fw() is only called from probe(), remove the lock. Fixes: bf394cc80369 ("iio: dac: adding support for Microchip MCP47FEB02") Signed-off-by: Felix Gu Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp47feb02.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index b218f0c3a0bd79..08fb8535969769 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -955,8 +955,6 @@ static int mcp47feb02_parse_fw(struct iio_dev *indio_dev, u32 num_channels; u8 chan_idx = 0; - guard(mutex)(&data->lock); - num_channels = device_get_child_node_count(dev); if (num_channels > chip_features->phys_channels) return dev_err_probe(dev, -EINVAL, "More channels than the chip supports\n"); From 630748afa7030b272b7bee5df857e7bcf132ed51 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Wed, 25 Feb 2026 11:06:00 +0100 Subject: [PATCH 020/700] iio: imu: st_lsm6dsx: Set FIFO ODR for accelerometer and gyroscope only The st_lsm6dsx_set_fifo_odr() function, which is called when enabling and disabling the hardware FIFO, checks the contents of the hw->settings->batch array at index sensor->id, and then sets the current ODR value in sensor registers that depend on whether the register address is set in the above array element. This logic is valid for internal sensors only, i.e. the accelerometer and gyroscope; however, since commit c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support"), this function is called also when configuring the hardware FIFO for external sensors (i.e. sensors accessed through the sensor hub functionality), which can result in unrelated device registers being written. Add a check to the beginning of st_lsm6dsx_set_fifo_odr() so that it does not touch any registers unless it is called for internal sensors. Fixes: c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support") Signed-off-by: Francesco Lavra Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 55d877745575c2..1ee2fc5f5f1f4a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -225,6 +225,10 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, const struct st_lsm6dsx_reg *batch_reg; u8 data; + /* Only internal sensors have a FIFO ODR configuration register. */ + if (sensor->id >= ARRAY_SIZE(hw->settings->batch)) + return 0; + batch_reg = &hw->settings->batch[sensor->id]; if (batch_reg->addr) { int val; From 679c04c10d65d32a3f269e696b22912ff0a001b9 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Wed, 25 Feb 2026 11:06:01 +0100 Subject: [PATCH 021/700] iio: imu: st_lsm6dsx: Set buffer sampling frequency for accelerometer only The st_lsm6dsx_hwfifo_odr_store() function, which is called when userspace writes the buffer sampling frequency sysfs attribute, calls st_lsm6dsx_check_odr(), which accesses the odr_table array at index `sensor->id`; since this array is only 2 entries long, an access for any sensor type other than accelerometer or gyroscope is an out-of-bounds access. The motivation for being able to set a buffer frequency different from the sensor sampling frequency is to support use cases that need accurate event detection (which requires a high sampling frequency) while retrieving sensor data at low frequency. Since all the supported event types are generated from acceleration data only, do not create the buffer sampling frequency attribute for sensor types other than the accelerometer. Fixes: 6b648a36c200 ("iio: imu: st_lsm6dsx: Decouple sensor ODR from FIFO batch data rate") Signed-off-by: Francesco Lavra Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 1ee2fc5f5f1f4a..5b28a3ffcc3d0a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -862,12 +862,21 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) int i, ret; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + const struct iio_dev_attr **attrs; + if (!hw->iio_devs[i]) continue; + /* + * For the accelerometer, allow setting FIFO sampling frequency + * values different from the sensor sampling frequency, which + * may be needed to keep FIFO data rate low while sampling + * acceleration data at high rates for accurate event detection. + */ + attrs = i == ST_LSM6DSX_ID_ACC ? st_lsm6dsx_buffer_attrs : NULL; ret = devm_iio_kfifo_buffer_setup_ext(hw->dev, hw->iio_devs[i], &st_lsm6dsx_buffer_ops, - st_lsm6dsx_buffer_attrs); + attrs); if (ret) return ret; } From edb11a1aef4011a4b7b22cc3c3396c6fe371f4a6 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:15 -0600 Subject: [PATCH 022/700] iio: gyro: mpu3050: Fix incorrect free_irq() variable The handler for the IRQ part of this driver is mpu3050->trig but, in the teardown free_irq() is called with handler mpu3050. Use correct IRQ handler when calling free_irq(). Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 317e7b217ec6b0..8df1f524d342fe 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1269,7 +1269,7 @@ void mpu3050_common_remove(struct device *dev) pm_runtime_disable(dev); iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) - free_irq(mpu3050->irq, mpu3050); + free_irq(mpu3050->irq, mpu3050->trig); iio_device_unregister(indio_dev); mpu3050_power_down(mpu3050); } From 4216db1043a3be72ef9c2b7b9f393d7fa72496e6 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:16 -0600 Subject: [PATCH 023/700] iio: gyro: mpu3050: Fix irq resource leak The interrupt handler is setup but only a few lines down if iio_trigger_register() fails the function returns without properly releasing the handler. Add cleanup goto to resolve resource leak. Detected by Smatch: drivers/iio/gyro/mpu3050-core.c:1128 mpu3050_trigger_probe() warn: 'irq' from request_threaded_irq() not released on lines: 1124. Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 8df1f524d342fe..d2f0899ac46b81 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1129,11 +1129,16 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) ret = iio_trigger_register(mpu3050->trig); if (ret) - return ret; + goto err_iio_trigger; indio_dev->trig = iio_trigger_get(mpu3050->trig); return 0; + +err_iio_trigger: + free_irq(mpu3050->irq, mpu3050->trig); + + return ret; } int mpu3050_common_probe(struct device *dev, From 4c05799449108fb0e0a6bd30e65fffc71e60db4d Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:17 -0600 Subject: [PATCH 024/700] iio: gyro: mpu3050: Move iio_device_register() to correct location iio_device_register() should be at the end of the probe function to prevent race conditions. Place iio_device_register() at the end of the probe function and place iio_device_unregister() accordingly. Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Suggested-by: Jonathan Cameron Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index d2f0899ac46b81..2e92daf047bd41 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1226,12 +1226,6 @@ int mpu3050_common_probe(struct device *dev, goto err_power_down; } - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "device register failed\n"); - goto err_cleanup_buffer; - } - dev_set_drvdata(dev, indio_dev); /* Check if we have an assigned IRQ to use as trigger */ @@ -1254,9 +1248,20 @@ int mpu3050_common_probe(struct device *dev, pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "device register failed\n"); + goto err_iio_device_register; + } + return 0; -err_cleanup_buffer: +err_iio_device_register: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + if (irq) + free_irq(mpu3050->irq, mpu3050->trig); iio_triggered_buffer_cleanup(indio_dev); err_power_down: mpu3050_power_down(mpu3050); @@ -1269,13 +1274,13 @@ void mpu3050_common_remove(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct mpu3050 *mpu3050 = iio_priv(indio_dev); + iio_device_unregister(indio_dev); pm_runtime_get_sync(dev); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) free_irq(mpu3050->irq, mpu3050->trig); - iio_device_unregister(indio_dev); mpu3050_power_down(mpu3050); } From d14116f6529fa085b1a1b1f224dc9604e4d2a29c Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 24 Feb 2026 16:48:18 -0600 Subject: [PATCH 025/700] iio: gyro: mpu3050: Fix out-of-sequence free_irq() The triggered buffer is initialized before the IRQ is requested. The removal path currently calls iio_triggered_buffer_cleanup() before free_irq(). This violates the expected LIFO. Place free_irq() in the correct location relative to iio_triggered_buffer_cleanup(). Fixes: 3904b28efb2c7 ("iio: gyro: Add driver for the MPU-3050 gyroscope") Suggested-by: Jonathan Cameron Reviewed-by: Linus Walleij Signed-off-by: Ethan Tidmore Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 2e92daf047bd41..d84e04e4b43142 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1278,9 +1278,9 @@ void mpu3050_common_remove(struct device *dev) pm_runtime_get_sync(dev); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); - iio_triggered_buffer_cleanup(indio_dev); if (mpu3050->irq) free_irq(mpu3050->irq, mpu3050->trig); + iio_triggered_buffer_cleanup(indio_dev); mpu3050_power_down(mpu3050); } From 20c2a46da4c65ac9c84f2876e2369a260b50e95b Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 23 Feb 2026 14:45:13 +0800 Subject: [PATCH 026/700] iio: adc: ad4062: Replace IRQF_ONESHOT with IRQF_NO_THREAD In ad4062_request_irq(), when request irq for "gp1", the code uses IRQF_ONESHOT flag, which is not appropriate for a primary handler that does not have a secondary threaded handler. And since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Since there is no threaded handler, replace devm_request_threaded_irq with devm_request_irq, and replace IRQF_ONESHOT with IRQF_NO_THREAD. Also remove an extraneous semicolon at the end of ad4062_write_raw_dispatch(). Found by code review, compile pass. Fixes: d5284402d28f ("iio: adc: Add support for ad4062") Signed-off-by: Felix Gu Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4062.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c index dd4ad32aa6f53c..8b03736d55fcc8 100644 --- a/drivers/iio/adc/ad4062.c +++ b/drivers/iio/adc/ad4062.c @@ -719,10 +719,8 @@ static int ad4062_request_irq(struct iio_dev *indio_dev) } st->gpo_irq[1] = true; - return devm_request_threaded_irq(dev, ret, - ad4062_irq_handler_drdy, - NULL, IRQF_ONESHOT, indio_dev->name, - indio_dev); + return devm_request_irq(dev, ret, ad4062_irq_handler_drdy, + IRQF_NO_THREAD, indio_dev->name, indio_dev); } static const struct iio_trigger_ops ad4062_trigger_ops = { @@ -955,7 +953,7 @@ static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2, default: return -EINVAL; } -}; +} static int ad4062_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, From c47ac75f5f24020cc0c8b835457a7637ad450939 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 20 Feb 2026 14:45:14 -0800 Subject: [PATCH 027/700] iio: hid-sensors: Use software trigger Recent changes linux mainline resulted in warning: "genirq: Warn about using IRQF_ONESHOT without a threaded handler" when HID sensor hub is used. When INDIO_BUFFER_TRIGGERED is used, the core attaches a poll function when enabling the buffer. This poll function uses request_threaded_irq() with both bottom half and top half handlers. But when using HID sensor hub, bottom half (thread handler) is not registered. In HID sensors, once a sensor is powered on, the hub collects samples and pushes data to the host when programmed thresholds are met. When this data is received for a sensor, it is pushed using iio_push_to_buffers_with_ts(). The sensor is powered ON or OFF based on the trigger callback set_trigger_state() when the poll function is attached. During the call to iio_triggered_buffer_setup_ext(), the HID sensor specifies only a handler function but provides no thread handler, as there is no data to read from the hub in thread context. Internally, this results in calling request_threaded_irq(). Recent kernel changes now warn when request_threaded_irq() is called without a thread handler. To address this issue, fundamental changes are required to avoid using iio_triggered_buffer_setup_ext(). HID sensors can use INDIO_BUFFER_SOFTWARE instead of INDIO_BUFFER_TRIGGERED, as this can work in trigger-less mode. In this approach, when user space opens the buffer, the sensor is powered on, and when the buffer is closed, the sensor is powered off using iio_buffer_setup_ops callbacks. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- .../common/hid-sensors/hid-sensor-trigger.c | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 5540e2d28f4acf..417c4ab8c1b27f 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "hid-sensor-trigger.h" static ssize_t _hid_sensor_set_report_latency(struct device *dev, @@ -202,12 +203,21 @@ static void hid_sensor_set_power_work(struct work_struct *work) _hid_sensor_power_state(attrb, true); } -static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +static int buffer_postenable(struct iio_dev *indio_dev) { - return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state); + return hid_sensor_power_state(iio_device_get_drvdata(indio_dev), 1); } +static int buffer_predisable(struct iio_dev *indio_dev) +{ + return hid_sensor_power_state(iio_device_get_drvdata(indio_dev), 0); +} + +static const struct iio_buffer_setup_ops hid_sensor_buffer_ops = { + .postenable = buffer_postenable, + .predisable = buffer_predisable, +}; + void hid_sensor_remove_trigger(struct iio_dev *indio_dev, struct hid_sensor_common *attrb) { @@ -219,14 +229,9 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); iio_trigger_free(attrb->trigger); - iio_triggered_buffer_cleanup(indio_dev); } EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, "IIO_HID"); -static const struct iio_trigger_ops hid_sensor_trigger_ops = { - .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state, -}; - int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, struct hid_sensor_common *attrb) { @@ -239,25 +244,34 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, else fifo_attrs = NULL; - ret = iio_triggered_buffer_setup_ext(indio_dev, - &iio_pollfunc_store_time, NULL, - IIO_BUFFER_DIRECTION_IN, - NULL, fifo_attrs); + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED; + + ret = devm_iio_kfifo_buffer_setup_ext(&indio_dev->dev, indio_dev, + &hid_sensor_buffer_ops, + fifo_attrs); if (ret) { - dev_err(&indio_dev->dev, "Triggered Buffer Setup Failed\n"); + dev_err(&indio_dev->dev, "Kfifo Buffer Setup Failed\n"); return ret; } + /* + * The current user space in distro "iio-sensor-proxy" is not working in + * trigerless mode and it expects + * /sys/bus/iio/devices/iio:device0/trigger/current_trigger. + * The change replacing iio_triggered_buffer_setup_ext() with + * devm_iio_kfifo_buffer_setup_ext() will not create attribute without + * registering a trigger with INDIO_HARDWARE_TRIGGERED. + * So the below code fragment is still required. + */ + trig = iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", name, iio_device_id(indio_dev)); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); - ret = -ENOMEM; - goto error_triggered_buffer_cleanup; + return -ENOMEM; } iio_trigger_set_drvdata(trig, attrb); - trig->ops = &hid_sensor_trigger_ops; ret = iio_trigger_register(trig); if (ret) { @@ -284,8 +298,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, iio_trigger_unregister(trig); error_free_trig: iio_trigger_free(trig); -error_triggered_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); return ret; } EXPORT_SYMBOL_NS(hid_sensor_setup_trigger, "IIO_HID"); From 773ef9f95385bae52dcb7fd129fefba3a71a04db Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Feb 2026 16:33:54 -0600 Subject: [PATCH 028/700] iio: imu: bno055: fix BNO055_SCAN_CH_COUNT off by one Fix an off-by-one error in the BNO055_SCAN_CH_COUNT macro. The count is derived by taking the difference of the last and first register addresses, dividing by the size of each channel (2 bytes). It needs to also add 1 to account for the fact that the count is inclusive of both the first and last channels. Thanks to the aligned_s64 timestamp field, there was already extra padding in the buffer, so there were no runtime issues caused by this bug. Fixes: 4aefe1c2bd0c ("iio: imu: add Bosch Sensortec BNO055 core driver") Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/bno055.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 303bc308f80a84..c96fec2ebb3e78 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -64,7 +64,7 @@ #define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E #define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30 #define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32 -#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2) +#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2 + 1) #define BNO055_TEMP_REG 0x34 #define BNO055_CALIB_STAT_REG 0x35 #define BNO055_CALIB_STAT_MAGN_SHIFT 0 From 8a9ebe8c3ca4c5bdad8f010656f4c2155da589fd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Feb 2026 17:48:22 -0800 Subject: [PATCH 029/700] gpio: timberdale: repair kernel-doc comments Use a ':' after struct member names to avoid kernel-doc warnings: Warning: include/linux/timb_gpio.h:22 struct member 'gpio_base' not described in 'timbgpio_platform_data' Warning: include/linux/timb_gpio.h:22 struct member 'nr_pins' not described in 'timbgpio_platform_data' Warning: include/linux/timb_gpio.h:22 struct member 'irq_base' not described in 'timbgpio_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260301014822.3133268-1-rdunlap@infradead.org Signed-off-by: Bartosz Golaszewski --- include/linux/timb_gpio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/timb_gpio.h b/include/linux/timb_gpio.h index 3faf5a6bb13e31..74f5e73bf6db67 100644 --- a/include/linux/timb_gpio.h +++ b/include/linux/timb_gpio.h @@ -9,10 +9,10 @@ /** * struct timbgpio_platform_data - Platform data of the Timberdale GPIO driver - * @gpio_base The number of the first GPIO pin, set to -1 for + * @gpio_base: The number of the first GPIO pin, set to -1 for * dynamic number allocation. - * @nr_pins Number of pins that is supported by the hardware (1-32) - * @irq_base If IRQ is supported by the hardware, this is the base + * @nr_pins: Number of pins that is supported by the hardware (1-32) + * @irq_base: If IRQ is supported by the hardware, this is the base * number of IRQ:s. One IRQ per pin will be used. Set to * -1 if IRQ:s is not supported. */ From 189645ba9cd9c1eed45151aacaae4347c1eb86a7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Feb 2026 17:48:11 -0800 Subject: [PATCH 030/700] gpio: nomadik: repair some kernel-doc comments Avoid these kernel-doc warnings by: - adding short descriptions for enums - using correct (matching) struct names in kernel-doc short descriptions - using the correct struct member name for @nfunctions Warning: include/linux/gpio/gpio-nomadik.h:116 missing initial short description on line: * enum prcm_gpiocr_reg_index Warning: include/linux/gpio/gpio-nomadik.h:125 missing initial short description on line: * enum prcm_gpiocr_altcx_index Warning: include/linux/gpio/gpio-nomadik.h:146 expecting prototype for struct prcm_gpio_altcx. Prototype was for struct prcm_gpiocr_altcx instead Warning: include/linux/gpio/gpio-nomadik.h:156 expecting prototype for struct prcm_gpio_altcx_pin_desc. Prototype was for struct prcm_gpiocr_altcx_pin_desc instead Warning: include/linux/gpio/gpio-nomadik.h:212 struct member 'nfunctions' not described in 'nmk_pinctrl_soc_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260301014811.3133250-1-rdunlap@infradead.org Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/gpio-nomadik.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/linux/gpio/gpio-nomadik.h b/include/linux/gpio/gpio-nomadik.h index 592a774a53cdf5..8061b98263611d 100644 --- a/include/linux/gpio/gpio-nomadik.h +++ b/include/linux/gpio/gpio-nomadik.h @@ -114,8 +114,7 @@ struct nmk_gpio_chip { } /** - * enum prcm_gpiocr_reg_index - * Used to reference an PRCM GPIOCR register address. + * enum prcm_gpiocr_reg_index - Used to reference a PRCM GPIOCR register address. */ enum prcm_gpiocr_reg_index { PRCM_IDX_GPIOCR1, @@ -123,8 +122,7 @@ enum prcm_gpiocr_reg_index { PRCM_IDX_GPIOCR3 }; /** - * enum prcm_gpiocr_altcx_index - * Used to reference an Other alternate-C function. + * enum prcm_gpiocr_altcx_index - Used to reference an Other alternate-C function. */ enum prcm_gpiocr_altcx_index { PRCM_IDX_GPIOCR_ALTC1, @@ -135,7 +133,7 @@ enum prcm_gpiocr_altcx_index { }; /** - * struct prcm_gpio_altcx - Other alternate-C function + * struct prcm_gpiocr_altcx - Other alternate-C function * @used: other alternate-C function availability * @reg_index: PRCM GPIOCR register index used to control the function * @control_bit: PRCM GPIOCR bit used to control the function @@ -147,7 +145,7 @@ struct prcm_gpiocr_altcx { } __packed; /** - * struct prcm_gpio_altcx_pin_desc - Other alternate-C pin + * struct prcm_gpiocr_altcx_pin_desc - Other alternate-C pin * @pin: The pin number * @altcx: array of other alternate-C[1-4] functions */ @@ -193,7 +191,7 @@ struct nmk_pingroup { * numbering. * @npins: The number of entries in @pins. * @functions: The functions supported on this SoC. - * @nfunction: The number of entries in @functions. + * @nfunctions: The number of entries in @functions. * @groups: An array describing all pin groups the pin SoC supports. * @ngroups: The number of entries in @groups. * @altcx_pins: The pins that support Other alternate-C function on this SoC From 36f6d4db3c5cb0f58fb02b1f54f9e86522d2f918 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 3 Mar 2026 00:00:04 +0800 Subject: [PATCH 031/700] iio: adc: ti-ads1119: Replace IRQF_ONESHOT with IRQF_NO_THREAD As there is no threaded handler, replace devm_request_threaded_irq() with devm_request_irq(), and as the handler calls iio_trigger_poll() which may not be called from a threaded handler replace IRQF_ONESHOT with IRQF_NO_THREAD. Since commit aef30c8d569c ("genirq: Warn about using IRQF_ONESHOT without a threaded handler"), the IRQ core checks IRQF_ONESHOT flag in IRQ request and gives a warning if there is no threaded handler. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 4454f28b2b58e3..7f771c7023b8fc 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -735,10 +735,8 @@ static int ads1119_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "Failed to setup IIO buffer\n"); if (client->irq > 0) { - ret = devm_request_threaded_irq(dev, client->irq, - ads1119_irq_handler, - NULL, IRQF_ONESHOT, - "ads1119", indio_dev); + ret = devm_request_irq(dev, client->irq, ads1119_irq_handler, + IRQF_NO_THREAD, "ads1119", indio_dev); if (ret) return dev_err_probe(dev, ret, "Failed to allocate irq\n"); From 56bd57e7b161f75535df91b229b0b2c64c6e5581 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Feb 2026 14:02:22 -0600 Subject: [PATCH 032/700] iio: add IIO_DECLARE_QUATERNION() macro Add a new IIO_DECLARE_QUATERNION() macro that is used to declare the field in an IIO buffer struct that contains a quaternion vector. Quaternions are currently the only IIO data type that uses the .repeat feature of struct iio_scan_type. This has an implicit rule that the element in the buffer must be aligned to the entire size of the repeated element. This macro will make that requirement explicit. Since this is the only user, we just call the macro IIO_DECLARE_QUATERNION() instead of something more generic. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index a9ecff191bd9d3..2c91b7659ce931 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -931,6 +931,18 @@ static inline void *iio_device_get_drvdata(const struct iio_dev *indio_dev) #define IIO_DECLARE_DMA_BUFFER_WITH_TS(type, name, count) \ __IIO_DECLARE_BUFFER_WITH_TS(type, name, count) __aligned(IIO_DMA_MINALIGN) +/** + * IIO_DECLARE_QUATERNION() - Declare a quaternion element + * @type: element type of the individual vectors + * @name: identifier name + * + * Quaternions are a vector composed of 4 elements (W, X, Y, Z). Use this macro + * to declare a quaternion element in a struct to ensure proper alignment in + * an IIO buffer. + */ +#define IIO_DECLARE_QUATERNION(type, name) \ + type name[4] __aligned(sizeof(type) * 4) + struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv); /* The information at the returned address is guaranteed to be cacheline aligned */ From 50d4cc74b8a720a9682a9c94f7e62a5de6b2ed3a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Feb 2026 14:02:23 -0600 Subject: [PATCH 033/700] iio: orientation: hid-sensor-rotation: fix quaternion alignment Restore the alignment of sampled_vals to 16 bytes by using IIO_DECLARE_QUATERNION(). This field contains a quaternion value which has scan_type.repeat = 4 and storagebits = 32. So the alignment must be 16 bytes to match the assumptions of iio_storage_bytes_for_si() and also to not break userspace. Reported-by: Lixu Zhang Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221077 Fixes: b31a74075cb4 ("iio: orientation: hid-sensor-rotation: remove unnecessary alignment") Tested-by: Lixu Zhang Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index e759f91a710a2c..6806481873be2d 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -19,7 +19,7 @@ struct dev_rot_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info quaternion; struct { - s32 sampled_vals[4]; + IIO_DECLARE_QUATERNION(s32, sampled_vals); aligned_s64 timestamp; } scan; int scale_pre_decml; From 398c0c8bbc8f5a9d2f43863275a427a9d3720b6f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 2 Mar 2026 16:59:55 -0500 Subject: [PATCH 034/700] dt-bindings: auxdisplay: ht16k33: Use unevaluatedProperties to fix common property warning Change additionalProperties to unevaluatedProperties because it refs to /schemas/input/matrix-keymap.yaml. Fix below CHECK_DTBS warnings: arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dtb: keypad@70 (holtek,ht16k33): 'keypad,num-columns', 'keypad,num-rows' do not match any of the regexes: '^pinctrl-[0-9]+$' from schema $id: http://devicetree.org/schemas/auxdisplay/holtek,ht16k33.yaml# Fixes: f12b457c6b25c ("dt-bindings: auxdisplay: ht16k33: Convert to json-schema") Acked-by: Rob Herring (Arm) Signed-off-by: Frank Li Signed-off-by: Andy Shevchenko --- .../devicetree/bindings/auxdisplay/holtek,ht16k33.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml index b90eec2077b4bd..fe1272e86467eb 100644 --- a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml +++ b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml @@ -66,7 +66,7 @@ then: required: - refresh-rate-hz -additionalProperties: false +unevaluatedProperties: false examples: - | From 5b2a16ab0dbd090dc545c05ee79a077cc7a9c1e0 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 2 Feb 2026 16:56:11 +0100 Subject: [PATCH 035/700] arm64: dts: qcom: monaco: Fix UART10 pinconf UART10 RTS and TX pins were incorrectly mapped to gpio84 and gpio85. Correct them to gpio85 (RTS) and gpio86 (TX) to match the hardware I/O mapping. Fixes: 467284a3097f ("arm64: dts: qcom: qcs8300: Add QUPv3 configuration") Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260202155611.1568-1-loic.poulain@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/monaco.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 5d2df4305d1c1c..dbb2273b0ee89b 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -6414,12 +6414,12 @@ }; qup_uart10_rts: qup-uart10-rts-state { - pins = "gpio84"; + pins = "gpio85"; function = "qup1_se2"; }; qup_uart10_tx: qup-uart10-tx-state { - pins = "gpio85"; + pins = "gpio86"; function = "qup1_se2"; }; From e2fa075d5ce1963e7cb7b0ac708ba567e5af66db Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 5 Mar 2026 11:21:52 -0800 Subject: [PATCH 036/700] iio: adc: ti-ads7950: normalize return value of gpio_get The GPIO get callback is expected to return 0 or 1 (or a negative error code). Ensure that the value returned by ti_ads7950_get() for output pins is normalized to the [0, 1] range. Fixes: 86ef402d805d ("gpiolib: sanitize the return value of gpio_chip::get()") Reviewed-by: Andy Shevchenko Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Dmitry Torokhov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index bbe1ce57778958..b8cc39fc39fba9 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -433,7 +433,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) /* If set as output, return the output */ if (st->gpio_cmd_settings_bitmask & BIT(offset)) { - ret = st->cmd_settings_bitmask & BIT(offset); + ret = (st->cmd_settings_bitmask & BIT(offset)) ? 1 : 0; goto out; } From d20bbae6e5d408a8a7c2a4344d76dd1ac557a149 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 5 Mar 2026 11:21:53 -0800 Subject: [PATCH 037/700] iio: adc: ti-ads7950: do not clobber gpio state in ti_ads7950_get() GPIO state was inadvertently overwritten by the result of spi_sync(), resulting in ti_ads7950_get() only returning 0 as GPIO state (or error). Fix this by introducing a separate variable to hold the state. Fixes: c97dce792dc8 ("iio: adc: ti-ads7950: add GPIO support") Reported-by: David Lechner Signed-off-by: Dmitry Torokhov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index b8cc39fc39fba9..cdc62488955933 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -427,13 +427,15 @@ static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) { struct ti_ads7950_state *st = gpiochip_get_data(chip); + bool state; int ret; mutex_lock(&st->slock); /* If set as output, return the output */ if (st->gpio_cmd_settings_bitmask & BIT(offset)) { - ret = (st->cmd_settings_bitmask & BIT(offset)) ? 1 : 0; + state = st->cmd_settings_bitmask & BIT(offset); + ret = 0; goto out; } @@ -444,7 +446,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) if (ret) goto out; - ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0; + state = (st->single_rx >> 12) & BIT(offset); /* Revert back to original settings */ st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA; @@ -456,7 +458,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) out: mutex_unlock(&st->slock); - return ret; + return ret ?: state; } static int ti_ads7950_get_direction(struct gpio_chip *chip, From 6f658d19a5a29a602c372e8cfe8d4a623367d211 Mon Sep 17 00:00:00 2001 From: Chunyang Chen Date: Thu, 5 Mar 2026 20:43:02 +0800 Subject: [PATCH 038/700] iio: adc: ti-ads1018: fix type overflow for data rate The variable 'drate' is currently defined as u8. However, the data rate values in ads1018 can reach up to 3300 Hz, which exceeds the maximum value of 255 that a u8 can hold. Change the type of 'drate' to u32 to match the data_rate_mode_to_hz array definition and ensure the data rate is handled correctly. Fixes: bf0bba486b5b ("iio: adc: Add ti-ads1018 driver") Signed-off-by: Chunyang Chen Reviewed-by: Andy Shevchenko Reviewed-by: Kurt Borja Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1018.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c index 6246b3cab71fd9..0780abd0d0db42 100644 --- a/drivers/iio/adc/ti-ads1018.c +++ b/drivers/iio/adc/ti-ads1018.c @@ -249,7 +249,7 @@ static int ads1018_single_shot(struct ads1018 *ads1018, struct iio_chan_spec const *chan, u16 *cnv) { u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1; - u8 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode]; + u32 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode]; u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode; struct spi_transfer xfer[2] = { { From 2f168094177f8553a36046afce139001801ca917 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 3 Mar 2026 21:47:33 +0800 Subject: [PATCH 039/700] iio: adc: ti-ads1119: Reinit completion before wait_for_completion_timeout() The completion is not reinit before wait_for_completion_timeout(), so wait_for_completion_timeout() will return immediately after the first successful completion. Fixes: a9306887eba4 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Felix Gu Reviewed-by: Francesco Dolcini Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1119.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 7f771c7023b8fc..79be71b4de963a 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -280,6 +280,9 @@ static int ads1119_single_conversion(struct ads1119_state *st, if (ret) goto pdown; + if (st->client->irq) + reinit_completion(&st->completion); + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); if (ret) goto pdown; From 7cf2f6ed8e7a3bf481ef70b6b4a2edb8abfa5c57 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Tue, 3 Mar 2026 10:38:26 +0800 Subject: [PATCH 040/700] iio: adc: aspeed: clear reference voltage bits before configuring vref Ensures the reference voltage bits are cleared in the ADC engine control register before configuring the voltage reference. This avoids potential misconfigurations caused by residual bits. Fixes: 1b5ceb55fec2 ("iio: adc: aspeed: Support ast2600 adc.") Signed-off-by: Billy Tsai Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 4be44c524b4daa..83a9885b9ae4da 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -415,6 +415,7 @@ static int aspeed_adc_vref_config(struct iio_dev *indio_dev) } adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_REF_VOLTAGE; ret = devm_regulator_get_enable_read_voltage(data->dev, "vref"); if (ret < 0 && ret != -ENODEV) From ea7e2e43d768102e2601dbbda42041c78d7a99f9 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 27 Feb 2026 14:20:46 +0200 Subject: [PATCH 041/700] iio: imu: adis16550: fix swapped gyro/accel filter functions The low-pass filter handlers for IIO_ANGL_VEL and IIO_ACCEL call each other's filter functions in both read_raw and write_raw. Swap them so each channel type uses its correct filter accessor. Fixes: bac4368fab62 ("iio: imu: adis16550: add adis16550 support") Signed-off-by: Antoniu Miclaus Acked-by: Robert Budai Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16550.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 28f0dbd0226cbe..1f2af506f4bdd5 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -643,12 +643,12 @@ static int adis16550_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: switch (chan->type) { case IIO_ANGL_VEL: - ret = adis16550_get_accl_filter_freq(st, val); + ret = adis16550_get_gyro_filter_freq(st, val); if (ret) return ret; return IIO_VAL_INT; case IIO_ACCEL: - ret = adis16550_get_gyro_filter_freq(st, val); + ret = adis16550_get_accl_filter_freq(st, val); if (ret) return ret; return IIO_VAL_INT; @@ -681,9 +681,9 @@ static int adis16550_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: switch (chan->type) { case IIO_ANGL_VEL: - return adis16550_set_accl_filter_freq(st, val); - case IIO_ACCEL: return adis16550_set_gyro_filter_freq(st, val); + case IIO_ACCEL: + return adis16550_set_accl_filter_freq(st, val); default: return -EINVAL; } From 73a505dc48144ec72e25874e2b2a72487b02d3bc Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 9 Mar 2026 10:39:49 +0100 Subject: [PATCH 042/700] thunderbolt: Fix property read in nhi_wake_supported() device_property_read_foo() returns 0 on success and only then modifies 'val'. Currently, val is left uninitialized if the aforementioned function returns non-zero, making nhi_wake_supported() return true almost always (random != 0) if the property is not present in device firmware. Invert the check to make it make sense. Fixes: 3cdb9446a117 ("thunderbolt: Add support for Intel Ice Lake") Cc: stable@vger.kernel.org Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index ccce020a2432fb..2bb2e79ca3cb35 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1020,7 +1020,7 @@ static bool nhi_wake_supported(struct pci_dev *pdev) * If power rails are sustainable for wakeup from S4 this * property is set by the BIOS. */ - if (device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) + if (!device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) return !!val; return true; From 57d2371d52be1d574b33382bfbf8052485b99d8b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 4 Mar 2026 08:30:12 +0800 Subject: [PATCH 043/700] tools/power turbostat: Fix illegal memory access when SMT is present and disabled When SMT is present and disabled, turbostat may under-size the thread_data array. This can corrupt results or cause turbostat to exit with a segmentation fault. [lenb: commit message] Fixes: a2b4d0f8bf07 ("tools/power turbostat: Favor cpu# over core#") Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 1a2671c28209e4..ae827485950d89 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -9702,13 +9702,12 @@ void allocate_counters(struct counters *counters) { int i; int num_cores = topo.cores_per_node * topo.nodes_per_pkg * topo.num_packages; - int num_threads = topo.threads_per_core * num_cores; - counters->threads = calloc(num_threads, sizeof(struct thread_data)); + counters->threads = calloc(topo.max_cpu_num + 1, sizeof(struct thread_data)); if (counters->threads == NULL) goto error; - for (i = 0; i < num_threads; i++) + for (i = 0; i < topo.max_cpu_num + 1; i++) (counters->threads)[i].cpu_id = -1; counters->cores = calloc(num_cores, sizeof(struct core_data)); From 6df6ea4b3d1567dbe6442f308735c23b63007c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Tue, 10 Mar 2026 20:44:03 +0000 Subject: [PATCH 044/700] gpiolib: clear requested flag if line is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If `gpiochip_line_is_valid()` fails, then `-EINVAL` is returned, but `desc->flags` will have `GPIOD_FLAG_REQUESTED` set, which will result in subsequent calls misleadingly returning `-EBUSY`. Fix that by clearing the flag in case of failure. Fixes: a501624864f3 ("gpio: Respect valid_mask when requesting GPIOs") Signed-off-by: Barnabás Pőcze Reviewed-by: Matti Vaittinen Link: https://patch.msgid.link/20260310204359.1202451-1-pobrn@protonmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ada572aaebd69d..9550500e1690be 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2465,8 +2465,10 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label) return -EBUSY; offset = gpiod_hwgpio(desc); - if (!gpiochip_line_is_valid(guard.gc, offset)) - return -EINVAL; + if (!gpiochip_line_is_valid(guard.gc, offset)) { + ret = -EINVAL; + goto out_clear_bit; + } /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. From 2619da73bb2f10d88f7e1087125c40144fdf0987 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 5 Mar 2026 20:49:55 +0100 Subject: [PATCH 045/700] KVM: x86: Use __DECLARE_FLEX_ARRAY() for UAPI structures with VLAs Commit 94dfc73e7cf4 ("treewide: uapi: Replace zero-length arrays with flexible-array members") broke the userspace API for C++. These structures ending in VLAs are typically a *header*, which can be followed by an arbitrary number of entries. Userspace typically creates a larger structure with some non-zero number of entries, for example in QEMU's kvm_arch_get_supported_msr_feature(): struct { struct kvm_msrs info; struct kvm_msr_entry entries[1]; } msr_data = {}; While that works in C, it fails in C++ with an error like: flexible array member 'kvm_msrs::entries' not at end of 'struct msr_data' Fix this by using __DECLARE_FLEX_ARRAY() for the VLA, which uses [0] for C++ compilation. Fixes: 94dfc73e7cf4 ("treewide: uapi: Replace zero-length arrays with flexible-array members") Cc: stable@vger.kernel.org Signed-off-by: David Woodhouse Link: https://patch.msgid.link/3abaf6aefd6e5efeff3b860ac38421d9dec908db.camel@infradead.org [sean: tag for stable@] Signed-off-by: Sean Christopherson --- arch/x86/include/uapi/asm/kvm.h | 12 ++++++------ include/uapi/linux/kvm.h | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 0d4538fa6c31ab..5f2b30d0405c87 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -197,13 +197,13 @@ struct kvm_msrs { __u32 nmsrs; /* number of msrs in entries */ __u32 pad; - struct kvm_msr_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_msr_entry, entries); }; /* for KVM_GET_MSR_INDEX_LIST */ struct kvm_msr_list { __u32 nmsrs; /* number of msrs in entries */ - __u32 indices[]; + __DECLARE_FLEX_ARRAY(__u32, indices); }; /* Maximum size of any access bitmap in bytes */ @@ -245,7 +245,7 @@ struct kvm_cpuid_entry { struct kvm_cpuid { __u32 nent; __u32 padding; - struct kvm_cpuid_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry, entries); }; struct kvm_cpuid_entry2 { @@ -267,7 +267,7 @@ struct kvm_cpuid_entry2 { struct kvm_cpuid2 { __u32 nent; __u32 padding; - struct kvm_cpuid_entry2 entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry2, entries); }; /* for KVM_GET_PIT and KVM_SET_PIT */ @@ -398,7 +398,7 @@ struct kvm_xsave { * the contents of CPUID leaf 0xD on the host. */ __u32 region[1024]; - __u32 extra[]; + __DECLARE_FLEX_ARRAY(__u32, extra); }; #define KVM_MAX_XCRS 16 @@ -566,7 +566,7 @@ struct kvm_pmu_event_filter { __u32 fixed_counter_bitmap; __u32 flags; __u32 pad[4]; - __u64 events[]; + __DECLARE_FLEX_ARRAY(__u64, events); }; #define KVM_PMU_EVENT_ALLOW 0 diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 80364d4dbebb0c..3f0d8d3c3dafd0 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -542,7 +543,7 @@ struct kvm_coalesced_mmio { struct kvm_coalesced_mmio_ring { __u32 first, last; - struct kvm_coalesced_mmio coalesced_mmio[]; + __DECLARE_FLEX_ARRAY(struct kvm_coalesced_mmio, coalesced_mmio); }; #define KVM_COALESCED_MMIO_MAX \ @@ -592,7 +593,7 @@ struct kvm_clear_dirty_log { /* for KVM_SET_SIGNAL_MASK */ struct kvm_signal_mask { __u32 len; - __u8 sigset[]; + __DECLARE_FLEX_ARRAY(__u8, sigset); }; /* for KVM_TPR_ACCESS_REPORTING */ @@ -1051,7 +1052,7 @@ struct kvm_irq_routing_entry { struct kvm_irq_routing { __u32 nr; __u32 flags; - struct kvm_irq_routing_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_irq_routing_entry, entries); }; #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) @@ -1142,7 +1143,7 @@ struct kvm_dirty_tlb { struct kvm_reg_list { __u64 n; /* number of regs */ - __u64 reg[]; + __DECLARE_FLEX_ARRAY(__u64, reg); }; struct kvm_one_reg { @@ -1608,7 +1609,7 @@ struct kvm_stats_desc { #ifdef __KERNEL__ char name[KVM_STATS_NAME_SIZE]; #else - char name[]; + __DECLARE_FLEX_ARRAY(char, name); #endif }; From 995a418a6ca33e466e5e1527663ae3d5eee18304 Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Thu, 12 Mar 2026 22:51:36 +0800 Subject: [PATCH 046/700] auxdisplay: lcd2s: add error handling for i2c transfers The lcd2s_print() and lcd2s_gotoxy() functions currently ignore the return value of lcd2s_i2c_master_send(), which can fail. This can lead to silent data loss or incorrect cursor positioning. Add proper error checking: if the number of bytes sent does not match the expected length, return -EIO; otherwise propagate any error code from the I2C transfer. Signed-off-by: Wang Jun <1742789905@qq.com> Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/lcd2s.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c index defb0573e43c69..c7a96272875245 100644 --- a/drivers/auxdisplay/lcd2s.c +++ b/drivers/auxdisplay/lcd2s.c @@ -99,8 +99,13 @@ static int lcd2s_print(struct charlcd *lcd, int c) { struct lcd2s_data *lcd2s = lcd->drvdata; u8 buf[2] = { LCD2S_CMD_WRITE, c }; + int ret; - lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; return 0; } @@ -108,9 +113,13 @@ static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) { struct lcd2s_data *lcd2s = lcd->drvdata; u8 buf[3] = { LCD2S_CMD_CUR_POS, y + 1, x + 1 }; + int ret; - lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); - + ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; return 0; } From dbbd550d7c8d90d3af9fe8a12a9caff077ddb8e3 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 14 Mar 2026 03:29:33 +0200 Subject: [PATCH 047/700] interconnect: qcom: sm8450: Fix NULL pointer dereference in icc_link_nodes() The change to dynamic IDs for SM8450 platform interconnects left two links unconverted, fix it to avoid the NULL pointer dereference in runtime, when a pointer to a destination interconnect is not valid: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008 <...> Call trace: icc_link_nodes+0x3c/0x100 (P) qcom_icc_rpmh_probe+0x1b4/0x528 platform_probe+0x64/0xc0 really_probe+0xc4/0x2a8 __driver_probe_device+0x80/0x140 driver_probe_device+0x48/0x170 __device_attach_driver+0xc0/0x148 bus_for_each_drv+0x88/0xf0 __device_attach+0xb0/0x1c0 device_initial_probe+0x58/0x68 bus_probe_device+0x40/0xb8 deferred_probe_work_func+0x90/0xd0 process_one_work+0x15c/0x3c0 worker_thread+0x2e8/0x400 kthread+0x150/0x208 ret_from_fork+0x10/0x20 Code: 900310f4 911d6294 91008280 94176078 (f94002a0) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception Fixes: 51513bec806f ("interconnect: qcom: sm8450: convert to dynamic IDs") Signed-off-by: Vladimir Zapolskiy Reviewed-by: Dmitry Baryshkov Link: https://msgid.link/20260314012933.350644-1-vladimir.zapolskiy@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sm8450.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c index 669a638bf3efcd..c88327d200acc8 100644 --- a/drivers/interconnect/qcom/sm8450.c +++ b/drivers/interconnect/qcom/sm8450.c @@ -800,7 +800,7 @@ static struct qcom_icc_node qhs_compute_cfg = { .channels = 1, .buswidth = 4, .num_links = 1, - .link_nodes = { MASTER_CDSP_NOC_CFG }, + .link_nodes = { &qhm_nsp_noc_config }, }; static struct qcom_icc_node qhs_cpr_cx = { @@ -874,7 +874,7 @@ static struct qcom_icc_node qhs_lpass_cfg = { .channels = 1, .buswidth = 4, .num_links = 1, - .link_nodes = { MASTER_CNOC_LPASS_AG_NOC }, + .link_nodes = { &qhm_config_noc }, }; static struct qcom_icc_node qhs_mss_cfg = { From 9978d74031f25fde575bef3e4e3e35c5009091ce Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 4 Mar 2026 22:14:32 +0800 Subject: [PATCH 048/700] iio: proximity: rfd77402: Fix completion race condition in IRQ mode In IRQ mode, the completion was being reinitialized after the measurement command had already been sent to the hardware. This created a race condition where the IRQ handler could call complete() before reinit_completion() was executed. Consequently, wait_for_completion_timeout() would fail to see the signal and wait until it timed out. Move reinit_completion() to occur before the measurement command is triggered to ensure the synchronization primitive is ready to capture the interrupt. Fixes: dc81be96a73a ("iio: proximity: rfd77402: Add interrupt handling support") Signed-off-by: Felix Gu Reviewed-by: Shrikant Raskar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/rfd77402.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c index 6afdbfca3e5a10..81b8daf17a5437 100644 --- a/drivers/iio/proximity/rfd77402.c +++ b/drivers/iio/proximity/rfd77402.c @@ -173,10 +173,8 @@ static int rfd77402_wait_for_result(struct rfd77402_data *data) struct i2c_client *client = data->client; int val, ret; - if (data->irq_en) { - reinit_completion(&data->completion); + if (data->irq_en) return rfd77402_wait_for_irq(data); - } /* * As per RFD77402 datasheet section '3.1.1 Single Measure', the @@ -204,6 +202,9 @@ static int rfd77402_measure(struct rfd77402_data *data) if (ret < 0) return ret; + if (data->irq_en) + reinit_completion(&data->completion); + ret = i2c_smbus_write_byte_data(client, RFD77402_CMD_R, RFD77402_CMD_SINGLE | RFD77402_CMD_VALID); From 79a86a6cc3669416a21fef32d0767d39ba84b3aa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:09 -0600 Subject: [PATCH 049/700] iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a hack to push two timestamps in the hid-sensor-rotation scan data to avoid breaking userspace applications that depend on the timestamp being at the incorrect location in the scan data due to unintentional misalignment in older kernels. When this driver was written, the timestamp was in the correct location because of the way iio_compute_scan_bytes() was implemented at the time. (Samples were 24 bytes each.) Then commit 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element") changed the computed scan_bytes to be a different size (32 bytes), which caused iio_push_to_buffers_with_timestamp() to place the timestamp at an incorrect offset. There have been long periods of time (6 years each) where the timestamp was in either location, so to not break either case, we open-code the timestamps to be pushed to both locations in the scan data. Reported-by: Jonathan Cameron Closes: https://lore.kernel.org/linux-iio/20260215162351.79f40b32@jic23-huawei/ Fixes: 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element") Signed-off-by: David Lechner Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 6806481873be2d..5a5e6e4fbe34ba 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -20,7 +20,12 @@ struct dev_rot_state { struct hid_sensor_hub_attribute_info quaternion; struct { IIO_DECLARE_QUATERNION(s32, sampled_vals); - aligned_s64 timestamp; + /* + * ABI regression avoidance: There are two copies of the same + * timestamp in case of userspace depending on broken alignment + * from older kernels. + */ + aligned_s64 timestamp[2]; } scan; int scale_pre_decml; int scale_post_decml; @@ -154,8 +159,19 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, if (!rot_state->timestamp) rot_state->timestamp = iio_get_time_ns(indio_dev); - iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan, - rot_state->timestamp); + /* + * ABI regression avoidance: IIO previously had an incorrect + * implementation of iio_push_to_buffers_with_timestamp() that + * put the timestamp in the last 8 bytes of the buffer, which + * was incorrect according to the IIO ABI. To avoid breaking + * userspace that may be depending on this broken behavior, we + * put the timestamp in both the correct place [0] and the old + * incorrect place [1]. + */ + rot_state->scan.timestamp[0] = rot_state->timestamp; + rot_state->scan.timestamp[1] = rot_state->timestamp; + + iio_push_to_buffers(indio_dev, &rot_state->scan); rot_state->timestamp = 0; } From c05a87d9ec3bf8727a5d746ce855003c6f2f8bb4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 9 Mar 2026 20:45:45 -0700 Subject: [PATCH 050/700] iio: imu: bmi160: Remove potential undefined behavior in bmi160_config_pin() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If 'pin' is not one of its expected values, the value of 'int_out_ctrl_shift' is undefined. With UBSAN enabled, this causes Clang to generate undefined behavior, resulting in the following warning: drivers/iio/imu/bmi160/bmi160_core.o: warning: objtool: bmi160_setup_irq() falls through to next function __cfi_bmi160_core_runtime_resume() Prevent the UB and improve error handling by returning an error if 'pin' has an unexpected value. While at it, simplify the code a bit by moving the 'pin_name' assignment to the first switch statement. Fixes: 895bf81e6bbf ("iio:bmi160: add drdy interrupt support") Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/a426d669-58bb-4be1-9eaa-6f3d83109e2d@app.fastmail.com Signed-off-by: Josh Poimboeuf Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi160/bmi160_core.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 5f47708b4c5dc5..4abb83b75e2e87 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -573,12 +573,16 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin, int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT; int_latch_mask = BMI160_INT1_LATCH_MASK; int_map_mask = BMI160_INT1_MAP_DRDY_EN; + pin_name = "INT1"; break; case BMI160_PIN_INT2: int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT; int_latch_mask = BMI160_INT2_LATCH_MASK; int_map_mask = BMI160_INT2_MAP_DRDY_EN; + pin_name = "INT2"; break; + default: + return -EINVAL; } int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift; @@ -612,17 +616,8 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin, ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP, int_map_mask, int_map_mask, write_usleep); - if (ret) { - switch (pin) { - case BMI160_PIN_INT1: - pin_name = "INT1"; - break; - case BMI160_PIN_INT2: - pin_name = "INT2"; - break; - } + if (ret) dev_err(dev, "Failed to configure %s IRQ pin", pin_name); - } return ret; } From dd154646d292cce7de952f216760c58c35cfecde Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Tue, 10 Mar 2026 13:56:44 +0200 Subject: [PATCH 051/700] iio: dac: mcp47feb02: Fix Vref validation [1-999] case Store reference voltages in uV instead of mV to avoid invalid error code in dev_err_probe() call. Vref variables store the actual value returned by devm_regulator_get_enable_read_voltage() function instead of the results of dividing it by MILLI. The corner case [1-999] divided by MILLI of the voltage reference variable value would become 0 is covered too. Fixes: bf394cc80369 ("iio: dac: adding support for Microchip MCP47FEB02") Link: https://lore.kernel.org/all/aYXvP5FLA5BvkoVX@stanley.mountain/ Signed-off-by: Ariana Lazar Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp47feb02.c | 49 ++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index 08fb8535969769..faccb804a5ed54 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -65,7 +65,7 @@ #define MCP47FEB02_MAX_SCALES_CH 3 #define MCP47FEB02_DAC_WIPER_UNLOCKED 0 #define MCP47FEB02_NORMAL_OPERATION 0 -#define MCP47FEB02_INTERNAL_BAND_GAP_mV 2440 +#define MCP47FEB02_INTERNAL_BAND_GAP_uV 2440000 #define NV_DAC_ADDR_OFFSET 0x10 enum mcp47feb02_vref_mode { @@ -697,44 +697,40 @@ static const struct iio_chan_spec mcp47febxx_ch_template = { }; static void mcp47feb02_init_scale(struct mcp47feb02_data *data, enum mcp47feb02_scale scale, - int vref_mV, int scale_avail[]) + int vref_uV, int scale_avail[]) { u32 value_micro, value_int; u64 tmp; - /* vref_mV should not be negative */ - tmp = (u64)vref_mV * MICRO >> data->chip_features->resolution; + /* vref_uV should not be negative */ + tmp = (u64)vref_uV * MILLI >> data->chip_features->resolution; value_int = div_u64_rem(tmp, MICRO, &value_micro); scale_avail[scale * 2] = value_int; scale_avail[scale * 2 + 1] = value_micro; } -static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_mV, - int vref_mV, int vref1_mV) +static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_uV, + int vref_uV, int vref1_uV) { - struct device *dev = regmap_get_device(data->regmap); int tmp_vref; - mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale); + mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_uV, data->scale); if (data->use_vref) - tmp_vref = vref_mV; + tmp_vref = vref_uV; else - tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV; + tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_uV; mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale); mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2, tmp_vref * 2, data->scale); if (data->phys_channels >= 4) { - mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale_1); - - if (data->use_vref1 && vref1_mV <= 0) - return dev_err_probe(dev, vref1_mV, "Invalid voltage for Vref1\n"); + mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_uV, data->scale_1); if (data->use_vref1) - tmp_vref = vref1_mV; + tmp_vref = vref1_uV; else - tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV; + tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_uV; mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale_1); @@ -1078,8 +1074,8 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data) return 0; } -static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV, - int vref_mV, int vref1_mV) +static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_uV, + int vref_uV, int vref1_uV) { unsigned int i; @@ -1087,7 +1083,7 @@ static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV, struct device *dev = regmap_get_device(data->regmap); int ret; - ret = mcp47feb02_init_scales_avail(data, vdd_mV, vref_mV, vref1_mV); + ret = mcp47feb02_init_scales_avail(data, vdd_uV, vref_uV, vref1_uV); if (ret) return dev_err_probe(dev, ret, "failed to init scales for ch %u\n", i); } @@ -1101,10 +1097,7 @@ static int mcp47feb02_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp47feb02_data *data; struct iio_dev *indio_dev; - int vref1_mV = 0; - int vref_mV = 0; - int vdd_mV; - int ret; + int vref1_uV, vref_uV, vdd_uV, ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -1141,13 +1134,14 @@ static int mcp47feb02_probe(struct i2c_client *client) if (ret < 0) return ret; - vdd_mV = ret / MILLI; + vdd_uV = ret; ret = devm_regulator_get_enable_read_voltage(dev, "vref"); if (ret > 0) { - vref_mV = ret / MILLI; + vref_uV = ret; data->use_vref = true; } else { + vref_uV = 0; dev_dbg(dev, "using internal band gap as voltage reference.\n"); dev_dbg(dev, "Vref is unavailable.\n"); } @@ -1155,9 +1149,10 @@ static int mcp47feb02_probe(struct i2c_client *client) if (chip_features->have_ext_vref1) { ret = devm_regulator_get_enable_read_voltage(dev, "vref1"); if (ret > 0) { - vref1_mV = ret / MILLI; + vref1_uV = ret; data->use_vref1 = true; } else { + vref1_uV = 0; dev_dbg(dev, "using internal band gap as voltage reference 1.\n"); dev_dbg(dev, "Vref1 is unavailable.\n"); } @@ -1167,7 +1162,7 @@ static int mcp47feb02_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "Error initialising vref register\n"); - ret = mcp47feb02_init_ch_scales(data, vdd_mV, vref_mV, vref1_mV); + ret = mcp47feb02_init_ch_scales(data, vdd_uV, vref_uV, vref1_uV); if (ret) return ret; From 641f6fda143b879da1515f821ee475073678cf2a Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 29 Jan 2026 20:53:20 +0530 Subject: [PATCH 052/700] soc: qcom: pd-mapper: Fix element length in servreg_loc_pfr_req_ei It looks element length declared in servreg_loc_pfr_req_ei for reason not matching servreg_loc_pfr_req's reason field due which we could observe decoding error on PD crash. qmi_decode_string_elem: String len 81 >= Max Len 65 Fix this by matching with servreg_loc_pfr_req's reason field. Fixes: 1ebcde047c54 ("soc: qcom: add pd-mapper implementation") Signed-off-by: Mukesh Ojha Reviewed-by: Dmitry Baryshkov Tested-by: Nikita Travkin Link: https://lore.kernel.org/r/20260129152320.3658053-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pdr_internal.h | 2 +- drivers/soc/qcom/qcom_pdr_msg.c | 2 +- include/linux/soc/qcom/pdr.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/pdr_internal.h b/drivers/soc/qcom/pdr_internal.h index 039508c1bbf7d0..047c0160b61788 100644 --- a/drivers/soc/qcom/pdr_internal.h +++ b/drivers/soc/qcom/pdr_internal.h @@ -84,7 +84,7 @@ struct servreg_set_ack_resp { struct servreg_loc_pfr_req { char service[SERVREG_NAME_LENGTH + 1]; - char reason[257]; + char reason[SERVREG_PFR_LENGTH + 1]; }; struct servreg_loc_pfr_resp { diff --git a/drivers/soc/qcom/qcom_pdr_msg.c b/drivers/soc/qcom/qcom_pdr_msg.c index ca98932140d873..02022b11ecf05f 100644 --- a/drivers/soc/qcom/qcom_pdr_msg.c +++ b/drivers/soc/qcom/qcom_pdr_msg.c @@ -325,7 +325,7 @@ const struct qmi_elem_info servreg_loc_pfr_req_ei[] = { }, { .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_len = SERVREG_PFR_LENGTH + 1, .elem_size = sizeof(char), .array_type = VAR_LEN_ARRAY, .tlv_type = 0x02, diff --git a/include/linux/soc/qcom/pdr.h b/include/linux/soc/qcom/pdr.h index 83a8ea612e69a3..2b7691e47c2a95 100644 --- a/include/linux/soc/qcom/pdr.h +++ b/include/linux/soc/qcom/pdr.h @@ -5,6 +5,7 @@ #include #define SERVREG_NAME_LENGTH 64 +#define SERVREG_PFR_LENGTH 256 struct pdr_service; struct pdr_handle; From d487085006109e5981e059476818243759d2e925 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 6 Mar 2026 12:20:14 +0100 Subject: [PATCH 053/700] soc: qcom: pmic_glink_altmode: Fix SVID=DP && unconnected edge case The commit referenced in Fixes started evaluating the value of alt_port->mux_ctrl before checking the active SVID. This led to drm_aux_hpd_bridge_notify() no longer being called for the 'DP unplug' case. Perhaps somewhat interestingly, the firmware sends a notification with SVID=DP, mux_ctrl=MUX_CTRL_STATE_NO_CONN and pin_assignment=0 on unplug. 'pin_assignment' was previously interpreted as a bitfield excerpt from the second byte of the DP pg_altmode payload (and stored as an u8). That value is used in pmic_glink_altmode_sc8280xp_notify(), decremented by 1 (DPAM_HPD_A). Previously, this would result in an u8 underflow that would rollover to 0xff (which prior to the Fixes patch would have caused a pmic_glink_altmode_safe() and 'disconnected' bridge notification). That check was removed, without a replacement. Resolve this issue by making sure the SID=DP && mux_ctrl=NO_CONN combo once again results in a HPD bridge notification. Fixes: 0539c5a6fdef ("soc: qcom: pmic_glink_altmode: Consume TBT3/USB4 mode notifications") Reported-by: Abel Vesa Tested-by: Abel Vesa Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260306-topic-pgaltmode_fixup-v1-1-ec154b2d8e89@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index d0afdcb96ee118..b496b88842a27e 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -350,15 +350,17 @@ static void pmic_glink_altmode_worker(struct work_struct *work) typec_switch_set(alt_port->typec_switch, alt_port->orientation); - if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { - pmic_glink_altmode_safe(altmode, alt_port); - } else if (alt_port->svid == USB_TYPEC_TBT_SID) { + if (alt_port->svid == USB_TYPEC_TBT_SID) { pmic_glink_altmode_enable_tbt(altmode, alt_port); } else if (alt_port->svid == USB_TYPEC_DP_SID) { - pmic_glink_altmode_enable_dp(altmode, alt_port, - alt_port->mode, - alt_port->hpd_state, - alt_port->hpd_irq); + if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { + pmic_glink_altmode_safe(altmode, alt_port); + } else { + pmic_glink_altmode_enable_dp(altmode, alt_port, + alt_port->mode, + alt_port->hpd_state, + alt_port->hpd_irq); + } if (alt_port->hpd_state) conn_status = connector_status_connected; @@ -368,6 +370,8 @@ static void pmic_glink_altmode_worker(struct work_struct *work) drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status); } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) { pmic_glink_altmode_enable_usb4(altmode, alt_port); + } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { + pmic_glink_altmode_safe(altmode, alt_port); } else { pmic_glink_altmode_enable_usb(altmode, alt_port); } From 01e8d0f742222f1e68f48180d5480097adf7ae9f Mon Sep 17 00:00:00 2001 From: Wanquan Zhong Date: Mon, 16 Mar 2026 19:55:12 +0800 Subject: [PATCH 054/700] USB: serial: option: add support for Rolling Wireless RW135R-GL Add VID/PID 33f8:1003 for the Rolling Wireless RW135R-GL M.2 module, which is used in laptop debug cards with MBIM interface for Linux/Chrome OS. The device supports mbim, pipe functionalities. Here are the outputs of usb-devices: T: Bus=04 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 2 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev=05.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C: #Ifs= 3 Cfg#= 1 Atr=a0 MxPwr=896mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=40 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms Signed-off-by: Wanquan Zhong Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e349ed66d2ac7b..d222bf2c253913 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2461,6 +2461,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, From 0e01c3416eb863ee7f156a9d7e7421ec0a9f68a0 Mon Sep 17 00:00:00 2001 From: Frej Drejhammar Date: Sun, 22 Feb 2026 18:00:42 +0100 Subject: [PATCH 055/700] USB: serial: io_edgeport: add support for Blackbox IC135A The Blackbox 724-746-5500 USB Director USB-RS-232 HUB, part number IC135A, is a rebadged Edgeport/4 with its own USB device id. Signed-off-by: Frej Drejhammar Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 3 +++ drivers/usb/serial/io_usbvend.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 58694b8943d194..3f5889145e5199 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -73,6 +73,7 @@ static const struct usb_device_id edgeport_4port_id_table[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { } }; @@ -121,6 +122,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, @@ -470,6 +472,7 @@ static void get_product_info(struct edgeport_serial *edge_serial) case ION_DEVICE_ID_EDGEPORT_2_DIN: case ION_DEVICE_ID_EDGEPORT_4_DIN: case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: + case ION_DEVICE_ID_BLACKBOX_IC135A: product_info->IsRS232 = 1; break; diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 9a6f742ad3abd0..c82a275e8e76ec 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -211,6 +211,7 @@ // // Definitions for other product IDs +#define ION_DEVICE_ID_BLACKBOX_IC135A 0x0801 // OEM device (rebranded Edgeport/4) #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device #define ION_DEVICE_ID_E5805A 0x1A01 // OEM device (rebranded Edgeport/4) From d56c9cca2e7f8f0ad91f37abd4e6357c2318b0db Mon Sep 17 00:00:00 2001 From: Even Xu Date: Tue, 17 Mar 2026 13:56:28 +0800 Subject: [PATCH 056/700] HID: Intel-thc-hid: Intel-quicki2c: Add NVL Device IDs Add Nova Lake THC QuickI2C device IDs to support list. Signed-off-by: Even Xu Signed-off-by: Jiri Kosina --- drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 7 +++++++ drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index f178017352ba72..46d3e9a019995f 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -26,6 +26,11 @@ static struct quicki2c_ddata ptl_ddata = { .max_interrupt_delay = MAX_RX_INTERRUPT_DELAY, }; +static struct quicki2c_ddata nvl_ddata = { + .max_detect_size = MAX_RX_DETECT_SIZE_NVL, + .max_interrupt_delay = MAX_RX_INTERRUPT_DELAY, +}; + /* THC QuickI2C ACPI method to get device properties */ /* HIDI2C device method */ static guid_t i2c_hid_guid = @@ -1032,6 +1037,8 @@ static const struct pci_device_id quicki2c_pci_tbl[] = { { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2, &ptl_ddata) }, { PCI_DEVICE_DATA(INTEL, THC_WCL_DEVICE_ID_I2C_PORT1, &ptl_ddata) }, { PCI_DEVICE_DATA(INTEL, THC_WCL_DEVICE_ID_I2C_PORT2, &ptl_ddata) }, + { PCI_DEVICE_DATA(INTEL, THC_NVL_H_DEVICE_ID_I2C_PORT1, &nvl_ddata) }, + { PCI_DEVICE_DATA(INTEL, THC_NVL_H_DEVICE_ID_I2C_PORT2, &nvl_ddata) }, { } }; MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 33a1e3db1cb2aa..61dbdece59a13f 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -15,6 +15,8 @@ #define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A #define PCI_DEVICE_ID_INTEL_THC_WCL_DEVICE_ID_I2C_PORT1 0x4D48 #define PCI_DEVICE_ID_INTEL_THC_WCL_DEVICE_ID_I2C_PORT2 0x4D4A +#define PCI_DEVICE_ID_INTEL_THC_NVL_H_DEVICE_ID_I2C_PORT1 0xD348 +#define PCI_DEVICE_ID_INTEL_THC_NVL_H_DEVICE_ID_I2C_PORT2 0xD34A /* Packet size value, the unit is 16 bytes */ #define MAX_PACKET_SIZE_VALUE_LNL 256 @@ -40,6 +42,8 @@ /* PTL Max packet size detection capability is 255 Bytes */ #define MAX_RX_DETECT_SIZE_PTL 255 +/* NVL Max packet size detection capability is 64K Bytes */ +#define MAX_RX_DETECT_SIZE_NVL 65535 /* Max interrupt delay capability is 2.56ms */ #define MAX_RX_INTERRUPT_DELAY 256 From 48e91af0cbe942d50ef6257d850accdca1d01378 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Tue, 17 Mar 2026 13:56:29 +0800 Subject: [PATCH 057/700] HID: Intel-thc-hid: Intel-quickspi: Add NVL Device IDs Add Nova Lake THC QuickSPI device IDs to support list. Signed-off-by: Even Xu Signed-off-by: Jiri Kosina --- drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c | 6 ++++++ drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index ad6bd59963b288..b6a69995692cba 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -37,6 +37,10 @@ struct quickspi_driver_data arl = { .max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL, }; +struct quickspi_driver_data nvl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, +}; + /* THC QuickSPI ACPI method to get device properties */ /* HIDSPI Method: {6e2ac436-0fcf-41af-a265-b32a220dcfab} */ static guid_t hidspi_guid = @@ -982,6 +986,8 @@ static const struct pci_device_id quickspi_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, THC_WCL_DEVICE_ID_SPI_PORT2, &ptl), }, {PCI_DEVICE_DATA(INTEL, THC_ARL_DEVICE_ID_SPI_PORT1, &arl), }, {PCI_DEVICE_DATA(INTEL, THC_ARL_DEVICE_ID_SPI_PORT2, &arl), }, + {PCI_DEVICE_DATA(INTEL, THC_NVL_H_DEVICE_ID_SPI_PORT1, &nvl), }, + {PCI_DEVICE_DATA(INTEL, THC_NVL_H_DEVICE_ID_SPI_PORT2, &nvl), }, {} }; MODULE_DEVICE_TABLE(pci, quickspi_pci_tbl); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index c30e1a42eb0984..bf5e18f5a5f425 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -23,6 +23,8 @@ #define PCI_DEVICE_ID_INTEL_THC_WCL_DEVICE_ID_SPI_PORT2 0x4D4B #define PCI_DEVICE_ID_INTEL_THC_ARL_DEVICE_ID_SPI_PORT1 0x7749 #define PCI_DEVICE_ID_INTEL_THC_ARL_DEVICE_ID_SPI_PORT2 0x774B +#define PCI_DEVICE_ID_INTEL_THC_NVL_H_DEVICE_ID_SPI_PORT1 0xD349 +#define PCI_DEVICE_ID_INTEL_THC_NVL_H_DEVICE_ID_SPI_PORT2 0xD34B /* HIDSPI special ACPI parameters DSM methods */ #define ACPI_QUICKSPI_REVISION_NUM 2 From b8ead30e2b2c7f32c8d2782e805160b110766592 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sat, 7 Mar 2026 21:43:11 -0500 Subject: [PATCH 058/700] tools/power turbostat: Fix swidle header vs data display I changed my mind about displaying swidle statistics, which are "added counters". Recently I reverted the column headers to 8-columns, but kept print_decimal_value() padding out to 16-columns for all 64-bit counters. Simplify by keeping print_decimial_value() at %lld -- which will often fit into 8-columns, and live with the fact that it can overflow and shift the other columns, which continue to tab-delimited. This is a better compromise than inserting a bunch of space characters that most users don't like. Fixes: 1a23ba6a1ba2 ("tools/power turbostat: Print wide names only for RAW 64-bit columns") Reported-by: Artem Bityutskiy Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index ae827485950d89..791b9154f662db 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -2852,10 +2852,9 @@ static inline int print_hex_value(int width, int *printed, char *delim, unsigned static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) { - if (width <= 32) - return (sprintf(outp, "%s%d", (*printed++ ? delim : ""), (unsigned int)value)); - else - return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + UNUSED(width); + + return (sprintf(outp, "%s%lld", (*printed++ ? delim : ""), value)); } static inline int print_float_value(int *printed, char *delim, double value) From 99b38fa34342b227a863948460b7937a97dbce28 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 10 Mar 2026 14:02:01 +0800 Subject: [PATCH 059/700] tools/power turbostat: Eliminate unnecessary data structure allocation Linux core_id's are a per-package namespace, not a per-node namespace. Rename topo.cores_per_node to topo.cores_per_pkg to reflect this. Eliminate topo.nodes_per_pkg from the sizing for core data structures, since it has no role except to unnecessarily bloat the allocation. Validated on multiple Intel platforms (ICX/SPR/SRF/EMR/GNR/CWF) with various CPU online/offline configurations and SMT enabled/disabled scenarios. No functional changes. [lenb: commit message] Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 791b9154f662db..14021a6ed71775 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -2409,7 +2409,7 @@ struct topo_params { int max_l3_id; int max_node_num; int nodes_per_pkg; - int cores_per_node; + int cores_per_pkg; int threads_per_core; } topo; @@ -9633,9 +9633,9 @@ void topology_probe(bool startup) topo.max_core_id = max_core_id; /* within a package */ topo.max_package_id = max_package_id; - topo.cores_per_node = max_core_id + 1; + topo.cores_per_pkg = max_core_id + 1; if (debug > 1) - fprintf(outf, "max_core_id %d, sizing for %d cores per package\n", max_core_id, topo.cores_per_node); + fprintf(outf, "max_core_id %d, sizing for %d cores per package\n", max_core_id, topo.cores_per_pkg); if (!summary_only) BIC_PRESENT(BIC_Core); @@ -9700,7 +9700,7 @@ void allocate_counters_1(struct counters *counters) void allocate_counters(struct counters *counters) { int i; - int num_cores = topo.cores_per_node * topo.nodes_per_pkg * topo.num_packages; + int num_cores = topo.cores_per_pkg * topo.num_packages; counters->threads = calloc(topo.max_cpu_num + 1, sizeof(struct thread_data)); if (counters->threads == NULL) From a444083286434ec1fd127c5da11a3091e6013008 Mon Sep 17 00:00:00 2001 From: Serhii Pievniev Date: Wed, 25 Feb 2026 18:16:03 -0500 Subject: [PATCH 060/700] tools/power/turbostat: Fix microcode patch level output for AMD/Hygon turbostat always used the same logic to read the microcode patch level, which is correct for Intel but not for AMD/Hygon. While Intel stores the patch level in the upper 32 bits of MSR, AMD stores it in the lower 32 bits, which causes turbostat to report the microcode version as 0x0 on AMD/Hygon. Fix by shifting right by 32 for non-AMD/Hygon, preserving the existing behavior for Intel and unknown vendors. Fixes: 3e4048466c39 ("tools/power turbostat: Add --no-msr option") Signed-off-by: Serhii Pievniev Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 14021a6ed71775..b985bce69142f6 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -9121,10 +9121,13 @@ void process_cpuid() cpuid_has_hv = ecx_flags & (1 << 31); if (!no_msr) { - if (get_msr(sched_getcpu(), MSR_IA32_UCODE_REV, &ucode_patch)) + if (get_msr(sched_getcpu(), MSR_IA32_UCODE_REV, &ucode_patch)) { warnx("get_msr(UCODE)"); - else + } else { ucode_patch_valid = true; + if (!authentic_amd && !hygon_genuine) + ucode_patch >>= 32; + } } /* @@ -9138,7 +9141,7 @@ void process_cpuid() if (!quiet) { fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d)", family, model, stepping, family, model, stepping); if (ucode_patch_valid) - fprintf(outf, " microcode 0x%x", (unsigned int)((ucode_patch >> 32) & 0xFFFFFFFF)); + fprintf(outf, " microcode 0x%x", (unsigned int)ucode_patch); fputc('\n', outf); fprintf(outf, "CPUID(0x80000000): max_extended_levels: 0x%x\n", max_extended_level); From bccaf98c1077f2216ecae12923e21ffaebd67d95 Mon Sep 17 00:00:00 2001 From: Max Krummenacher Date: Thu, 19 Feb 2026 16:11:49 +0100 Subject: [PATCH 061/700] Revert "ARM: dts: imx: move nand related property under nand@0" This reverts commit 8124b4a4a96b57d6cc3705a9df9623c52baa047b. The change introduced a regression: at least Colibri iMX6ULL and Colibri iMX7 no longer boot with that commit applied, while they boot again after reverting it. Although this has only been verified on these two modules, the issue is expected to affect all device trees using the gpmi-nand driver. [ 0.876938] Creating 5 MTD partitions on "gpmi-nand": [ 0.876974] 0x000000000000-0x000000080000 : "mx7-bcb" [ 0.879860] 0x000000080000-0x000000200000 : "u-boot1" [ 0.884761] 0x000000200000-0x000000380000 : "u-boot2" [ 0.886993] 0x000000380000-0x000000400000 : "u-boot-env" [ 0.894686] 0x000000400000-0x000020000000 : "ubi" [ 0.899054] gpmi-nand 33002000.nand-controller: driver registered. ... [ 0.960443] ubi0: default fastmap pool size: 200 [ 0.960476] ubi0: default fastmap WL pool size: 100 [ 0.960500] ubi0: attaching mtd4 [ 1.636355] ubi0 error: scan_peb: bad image sequence number 1588722158 in PEB 4060, expected 1574791632 ... [ 1.649889] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd4, error -22 [ 1.650029] UBI error: cannot attach mtd4 ... [ 1.670262] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,253) Fixes: 8124b4a4a96b ("ARM: dts: imx: move nand related property under nand@0") Signed-off-by: Max Krummenacher Signed-off-by: Frank Li --- arch/arm/boot/dts/nxp/imx/imx6-logicpd-som.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6qdl-icore.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-pfla02.dtsi | 6 +----- .../boot/dts/nxp/imx/imx6qdl-phytec-phycore-som.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6ul-geam.dts | 6 +----- arch/arm/boot/dts/nxp/imx/imx6ul-isiot.dtsi | 6 +----- .../boot/dts/nxp/imx/imx6ul-phytec-phycore-som.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi | 12 ++++-------- .../boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi | 12 ++++-------- arch/arm/boot/dts/nxp/imx/imx6ull-myir-mys-6ulx.dtsi | 6 +----- arch/arm/boot/dts/nxp/imx/imx6ulz-bsh-smm-m2.dts | 6 +----- arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi | 8 ++------ 15 files changed, 22 insertions(+), 82 deletions(-) diff --git a/arch/arm/boot/dts/nxp/imx/imx6-logicpd-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6-logicpd-som.dtsi index f452764fae00ef..547fb141ec0c9f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6-logicpd-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6-logicpd-som.dtsi @@ -36,12 +36,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c3 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-icore.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-icore.dtsi index 58ecdb87c6d404..9975b6ee433d1d 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-icore.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-icore.dtsi @@ -172,12 +172,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-pfla02.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-pfla02.dtsi index 6f3becd33a5b5f..aa9a442852f41b 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-pfla02.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-pfla02.dtsi @@ -102,12 +102,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-phycore-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-phycore-som.dtsi index f2140dd8525f81..85e278eb201610 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-phycore-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-phytec-phycore-som.dtsi @@ -73,12 +73,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "disabled"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c3 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi index 131a3428ddb867..c93dbc595ef6ec 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi @@ -260,14 +260,10 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; #address-cells = <1>; #size-cells = <0>; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c3 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi index d29adfef5fdba3..57297d6521cf09 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tx6.dtsi @@ -252,13 +252,9 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; fsl,no-blockmark-swap; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-geam.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-geam.dts index 40d530c1dc29f2..2a6bb5ff808add 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-geam.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-geam.dts @@ -133,12 +133,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-isiot.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-isiot.dtsi index 776f6f78ee4631..e34c8cbe36aec6 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-isiot.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-isiot.dtsi @@ -101,12 +101,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "disabled"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-phytec-phycore-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-phytec-phycore-som.dtsi index 27e4d2aec137fc..a3ea1b20846253 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-phytec-phycore-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-phytec-phycore-som.dtsi @@ -63,12 +63,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "disabled"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi index dc53f9286ffe27..1992dfb53b45cd 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tx6ul.dtsi @@ -296,13 +296,9 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; fsl,no-blockmark-swap; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &i2c2 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi index eaed2cbf0c82aa..ec3c1e7301f48f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-colibri.dtsi @@ -160,15 +160,11 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; fsl,use-minimum-ecc; + nand-on-flash-bbt; + nand-ecc-mode = "hw"; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - nand-ecc-mode = "hw"; - nand-ecc-strength = <8>; - nand-ecc-step-size = <512>; - }; }; /* I2C3_SDA/SCL on SODIMM 194/196 (e.g. RTC on carrier board) */ diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi index 3dfd43b320553c..43518bf0760249 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea.dtsi @@ -43,15 +43,11 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <0>; + nand-ecc-step-size = <0>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-mode = "hw"; - nand-ecc-strength = <0>; - nand-ecc-step-size = <0>; - nand-on-flash-bbt; - }; }; &iomuxc { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-myir-mys-6ulx.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-myir-mys-6ulx.dtsi index fc298f57bfff34..83b9de17cee2de 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-myir-mys-6ulx.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-myir-mys-6ulx.dtsi @@ -60,12 +60,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "disabled"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &uart1 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ulz-bsh-smm-m2.dts b/arch/arm/boot/dts/nxp/imx/imx6ulz-bsh-smm-m2.dts index 8ec18eae98a46a..2d9f495660c9a3 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ulz-bsh-smm-m2.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ulz-bsh-smm-m2.dts @@ -25,12 +25,8 @@ &gpmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; + nand-on-flash-bbt; status = "okay"; - - nand@0 { - reg = <0>; - nand-on-flash-bbt; - }; }; &snvs_poweroff { diff --git a/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi b/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi index a41dc4edfc0ddc..8666dcd7fe9746 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7-colibri.dtsi @@ -375,14 +375,10 @@ /* NAND on such SKUs */ &gpmi { fsl,use-minimum-ecc; + nand-ecc-mode = "hw"; + nand-on-flash-bbt; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; - - nand@0 { - reg = <0>; - nand-ecc-mode = "hw"; - nand-on-flash-bbt; - }; }; /* On-module Power I2C */ From 4cd46ea0eb4504f7f4fea92cb4601c5c9a3e545e Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Sat, 21 Feb 2026 19:15:18 +0100 Subject: [PATCH 062/700] Revert "arm64: dts: imx8mq-librem5: Set the DVS voltages lower" This reverts commit c24a9b698fb02cd0723fa8375abab07f94b97b10. It's been found that there's a significant per-unit variance in accepted supply voltages and the current set still makes some units unstable. Revert back to nominal values. Cc: stable@vger.kernel.org Fixes: c24a9b698fb0 ("arm64: dts: imx8mq-librem5: Set the DVS voltages lower") Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Frank Li --- .../boot/dts/freescale/imx8mq-librem5-r3.dts | 2 +- .../boot/dts/freescale/imx8mq-librem5.dtsi | 22 +++++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5-r3.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5-r3.dts index 077c5cd2586f75..4533a84fb0b95f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5-r3.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5-r3.dts @@ -7,7 +7,7 @@ &a53_opp_table { opp-1000000000 { - opp-microvolt = <950000>; + opp-microvolt = <1000000>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi index eee390c2721050..7818d84f25a7e0 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi @@ -880,8 +880,8 @@ regulator-max-microvolt = <1300000>; regulator-boot-on; regulator-ramp-delay = <1250>; - rohm,dvs-run-voltage = <880000>; - rohm,dvs-idle-voltage = <820000>; + rohm,dvs-run-voltage = <900000>; + rohm,dvs-idle-voltage = <850000>; rohm,dvs-suspend-voltage = <810000>; regulator-always-on; }; @@ -892,8 +892,8 @@ regulator-max-microvolt = <1300000>; regulator-boot-on; regulator-ramp-delay = <1250>; - rohm,dvs-run-voltage = <950000>; - rohm,dvs-idle-voltage = <850000>; + rohm,dvs-run-voltage = <1000000>; + rohm,dvs-idle-voltage = <900000>; regulator-always-on; }; @@ -902,14 +902,14 @@ regulator-min-microvolt = <700000>; regulator-max-microvolt = <1300000>; regulator-boot-on; - rohm,dvs-run-voltage = <850000>; + rohm,dvs-run-voltage = <900000>; }; buck4_reg: BUCK4 { regulator-name = "buck4"; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1300000>; - rohm,dvs-run-voltage = <930000>; + rohm,dvs-run-voltage = <1000000>; }; buck5_reg: BUCK5 { @@ -1448,13 +1448,3 @@ fsl,ext-reset-output; status = "okay"; }; - -&a53_opp_table { - opp-1000000000 { - opp-microvolt = <850000>; - }; - - opp-1500000000 { - opp-microvolt = <950000>; - }; -}; From 511f76bf1dce5acf8907b65a7d1bc8f7e7c0d637 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Sat, 21 Feb 2026 19:15:19 +0100 Subject: [PATCH 063/700] arm64: dts: imx8mq-librem5: Bump BUCK1 suspend voltage up to 0.85V MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The minimal voltage of VDD_SOC sourced from BUCK1 is 0.81V, which is the currently set value. However, BD71837 only guarantees accuracy of ±0.01V, and this still doesn't factor other reasons for actual voltage to slightly drop in, resulting in the possibility of running out of the operational range. Bump the voltage up to 0.85V, which should give enough headroom. Cc: stable@vger.kernel.org Fixes: 8f0216b006e5 ("arm64: dts: Add a device tree for the Librem 5 phone") Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Frank Li --- arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi index 7818d84f25a7e0..f5d529c5baf3e6 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi @@ -882,7 +882,7 @@ regulator-ramp-delay = <1250>; rohm,dvs-run-voltage = <900000>; rohm,dvs-idle-voltage = <850000>; - rohm,dvs-suspend-voltage = <810000>; + rohm,dvs-suspend-voltage = <850000>; regulator-always-on; }; From ed532d738a82269dfe490436b9b8def2274cfb6e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Mar 2026 11:00:31 +0200 Subject: [PATCH 064/700] tools/power turbostat: Consistently use print_float_value() Fix the PMT thread code to use print_float_value(), to be consistent with the PMT core and package code. [lenb: commit message] Signed-off-by: Artem Bityutskiy Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index b985bce69142f6..9744f9caac9af9 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -3489,12 +3489,12 @@ int format_counters(PER_THREAD_PARAMS) case PMT_TYPE_XTAL_TIME: value_converted = pct(value_raw / crystal_hz, interval_float); - outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + outp += print_float_value(&printed, delim, value_converted); break; case PMT_TYPE_TCORE_CLOCK: value_converted = pct(value_raw / tcore_clock_freq_hz, interval_float); - outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + outp += print_float_value(&printed, delim, value_converted); } } From 23cb4f5c81766e70e5f32ed0987ee8fb5ab2e00a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Mar 2026 11:00:32 +0200 Subject: [PATCH 065/700] tools/power turbostat: Fix incorrect format variable In the perf thread, core, and package counter loops, an incorrect 'mp->format' variable is used instead of 'pp->format'. [lenb: edit commit message] Fixes: 696d15cbd8c2 ("tools/power turbostat: Refactor floating point printout code") Signed-off-by: Artem Bityutskiy Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 9744f9caac9af9..4d954533c71de7 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -3468,7 +3468,7 @@ int format_counters(PER_THREAD_PARAMS) for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) { if (pp->format == FORMAT_RAW) outp += print_hex_value(pp->width, &printed, delim, t->perf_counter[i]); - else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + else if (pp->format == FORMAT_DELTA || pp->format == FORMAT_AVERAGE) outp += print_decimal_value(pp->width, &printed, delim, t->perf_counter[i]); else if (pp->format == FORMAT_PERCENT) { if (pp->type == COUNTER_USEC) @@ -3538,7 +3538,7 @@ int format_counters(PER_THREAD_PARAMS) for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) { if (pp->format == FORMAT_RAW) outp += print_hex_value(pp->width, &printed, delim, c->perf_counter[i]); - else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + else if (pp->format == FORMAT_DELTA || pp->format == FORMAT_AVERAGE) outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); else if (pp->format == FORMAT_PERCENT) outp += print_float_value(&printed, delim, pct(c->perf_counter[i], tsc)); @@ -3694,7 +3694,7 @@ int format_counters(PER_THREAD_PARAMS) outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); else if (pp->type == COUNTER_K2M) outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); - else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + else if (pp->format == FORMAT_DELTA || pp->format == FORMAT_AVERAGE) outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); else if (pp->format == FORMAT_PERCENT) outp += print_float_value(&printed, delim, pct(p->perf_counter[i], tsc)); From b6398bc2ef3a78f1be37ba01ae0a5eedaee47803 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Mar 2026 11:00:33 +0200 Subject: [PATCH 066/700] tools/power turbostat: Fix --show/--hide for individual cpuidle counters Problem: individual swidle counter names (C1, C1+, C1-, etc.) cannot be selected via --show/--hide due to two bugs in probe_cpuidle_counts(): 1. The function returns immediately when BIC_cpuidle is not enabled, without checking deferred_add_index. 2. The deferred name check runs against name_buf before the trailing newline is stripped, so is_deferred_add("C1\n") never matches "C1". Fix: 1. Relax the early return to pass through when deferred names are queued. 2. Strip the trailing newline from name_buf before performing deferred name checks. 3. Check each suffixed variant (C1+, C1, C1-) individually so that e.g. "--show C1+" enables only the requested metric. In addition, introduce a helper function to avoid repeating the condition (readability cleanup). Fixes: ec4acd3166d8 ("tools/power turbostat: disable "cpuidle" invocation counters, by default") Signed-off-by: Artem Bityutskiy Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 35 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 4d954533c71de7..3487548841e1d7 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -11285,6 +11285,14 @@ void probe_cpuidle_residency(void) } } +static bool cpuidle_counter_wanted(char *name) +{ + if (is_deferred_skip(name)) + return false; + + return DO_BIC(BIC_cpuidle) || is_deferred_add(name); +} + void probe_cpuidle_counts(void) { char path[64]; @@ -11294,7 +11302,7 @@ void probe_cpuidle_counts(void) int min_state = 1024, max_state = 0; char *sp; - if (!DO_BIC(BIC_cpuidle)) + if (!DO_BIC(BIC_cpuidle) && !deferred_add_index) return; for (state = 10; state >= 0; --state) { @@ -11309,12 +11317,6 @@ void probe_cpuidle_counts(void) remove_underbar(name_buf); - if (!DO_BIC(BIC_cpuidle) && !is_deferred_add(name_buf)) - continue; - - if (is_deferred_skip(name_buf)) - continue; - /* truncate "C1-HSW\n" to "C1", or truncate "C1\n" to "C1" */ sp = strchr(name_buf, '-'); if (!sp) @@ -11329,16 +11331,19 @@ void probe_cpuidle_counts(void) * Add 'C1+' for C1, and so on. The 'below' sysfs file always contains 0 for * the last state, so do not add it. */ - *sp = '+'; *(sp + 1) = '\0'; - sprintf(path, "cpuidle/state%d/below", state); - add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + if (cpuidle_counter_wanted(name_buf)) { + sprintf(path, "cpuidle/state%d/below", state); + add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + } } *sp = '\0'; - sprintf(path, "cpuidle/state%d/usage", state); - add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + if (cpuidle_counter_wanted(name_buf)) { + sprintf(path, "cpuidle/state%d/usage", state); + add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + } /* * The 'above' sysfs file always contains 0 for the shallowest state (smallest @@ -11347,8 +11352,10 @@ void probe_cpuidle_counts(void) if (state != min_state) { *sp = '-'; *(sp + 1) = '\0'; - sprintf(path, "cpuidle/state%d/above", state); - add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + if (cpuidle_counter_wanted(name_buf)) { + sprintf(path, "cpuidle/state%d/above", state); + add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0); + } } } } From 85d98669fa7f1d3041d962515e45ee6e392db6f8 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 2 Mar 2026 15:26:03 +0100 Subject: [PATCH 067/700] arm64: dts: qcom: monaco: Reserve full Gunyah metadata region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We observe spurious "Synchronous External Abort" exceptions (ESR=0x96000010) and kernel crashes on Monaco-based platforms. These faults are caused by the kernel inadvertently accessing hypervisor-owned memory that is not properly marked as reserved. >From boot log, The Qualcomm hypervisor reports the memory range at 0x91a80000 of size 0x80000 (512 KiB) as hypervisor-owned: qhee_hyp_assign_remove_memory: 0x91a80000/0x80000 -> ret 0 However, the EFI memory map provided by firmware only reserves the subrange 0x91a40000–0x91a87fff (288 KiB). The remaining portion (0x91a88000–0x91afffff) is incorrectly reported as conventional memory (from efi debug): efi: 0x000091a40000-0x000091a87fff [Reserved...] efi: 0x000091a88000-0x0000938fffff [Conventional...] As a result, the allocator may hand out PFNs inside the hypervisor owned region, causing fatal aborts when the kernel accesses those addresses. Add a reserved-memory carveout for the Gunyah hypervisor metadata at 0x91a80000 (512 KiB) and mark it as no-map so Linux does not map or allocate from this area. For the record: Hyp version: gunyah-e78adb36e debug (2025-11-17 05:38:05 UTC) UEFI Ver: 6.0.260122.BOOT.MXF.1.0.c1-00449-KODIAKLA-1 Fixes: 7be190e4bdd2 ("arm64: dts: qcom: add QCS8300 platform") Signed-off-by: Loic Poulain Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260302142603.1113355-1-loic.poulain@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/monaco.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index dbb2273b0ee89b..0cb9fd154b684e 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -765,6 +765,11 @@ hwlocks = <&tcsr_mutex 3>; }; + gunyah_md_mem: gunyah-md-region@91a80000 { + reg = <0x0 0x91a80000 0x0 0x80000>; + no-map; + }; + lpass_machine_learning_mem: lpass-machine-learning-region@93b00000 { reg = <0x0 0x93b00000 0x0 0xf00000>; no-map; From e1eabb072c75681f78312c484ccfffb7430f206e Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Wed, 11 Mar 2026 17:12:15 +0800 Subject: [PATCH 068/700] usb: gadget: u_ether: Fix race between gether_disconnect and eth_stop A race condition between gether_disconnect() and eth_stop() leads to a NULL pointer dereference. Specifically, if eth_stop() is triggered concurrently while gether_disconnect() is tearing down the endpoints, eth_stop() attempts to access the cleared endpoint descriptor, causing the following NPE: Unable to handle kernel NULL pointer dereference Call trace: __dwc3_gadget_ep_enable+0x60/0x788 dwc3_gadget_ep_enable+0x70/0xe4 usb_ep_enable+0x60/0x15c eth_stop+0xb8/0x108 Because eth_stop() crashes while holding the dev->lock, the thread running gether_disconnect() fails to acquire the same lock and spins forever, resulting in a hardlockup: Core - Debugging Information for Hardlockup core(7) Call trace: queued_spin_lock_slowpath+0x94/0x488 _raw_spin_lock+0x64/0x6c gether_disconnect+0x19c/0x1e8 ncm_set_alt+0x68/0x1a0 composite_setup+0x6a0/0xc50 The root cause is that the clearing of dev->port_usb in gether_disconnect() is delayed until the end of the function. Move the clearing of dev->port_usb to the very beginning of gether_disconnect() while holding dev->lock. This cuts off the link immediately, ensuring eth_stop() will see dev->port_usb as NULL and safely bail out. Fixes: 2b3d942c4878 ("usb ethernet gadget: split out network core") Cc: stable Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260311-gether-disconnect-npe-v1-1-454966adf7c7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 1a9e7c495e2e87..23c7c0cdf20295 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1223,6 +1223,11 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); + spin_lock(&dev->lock); + dev->port_usb = NULL; + link->is_suspend = false; + spin_unlock(&dev->lock); + netif_stop_queue(dev->net); netif_carrier_off(dev->net); @@ -1260,11 +1265,6 @@ void gether_disconnect(struct gether *link) dev->header_len = 0; dev->unwrap = NULL; dev->wrap = NULL; - - spin_lock(&dev->lock); - dev->port_usb = NULL; - link->is_suspend = false; - spin_unlock(&dev->lock); } EXPORT_SYMBOL_GPL(gether_disconnect); From 8a768552f7a8276fb9e01d49773d2094ace7c8f1 Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Thu, 12 Mar 2026 09:27:28 -0300 Subject: [PATCH 069/700] usb: usbtmc: Flush anchored URBs in usbtmc_release When calling usbtmc_release, pending anchored URBs must be flushed or killed to prevent use-after-free errors (e.g. in the HCD giveback path). Call usbtmc_draw_down() to allow anchored URBs to be completed. Fixes: 4f3c8d6eddc2 ("usb: usbtmc: Support Read Status Byte with SRQ per file") Reported-by: syzbot+9a3c54f52bd1edbd975f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9a3c54f52bd1edbd975f Cc: stable Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260312-usbtmc-flush-release-v1-1-5755e9f4336f@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index d39bbfd7fd18a9..bd9347804dec6d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -254,6 +254,9 @@ static int usbtmc_release(struct inode *inode, struct file *file) list_del(&file_data->file_elem); spin_unlock_irq(&file_data->data->dev_lock); + + /* flush anchored URBs */ + usbtmc_draw_down(file_data); mutex_unlock(&file_data->data->io_mutex); kref_put(&file_data->data->kref, usbtmc_delete); From d2d8c17ac01a1b1f638ea5d340a884ccc5015186 Mon Sep 17 00:00:00 2001 From: Nathan Rebello Date: Fri, 13 Mar 2026 18:24:53 -0400 Subject: [PATCH 070/700] usb: typec: ucsi: validate connector number in ucsi_notify_common() The connector number extracted from CCI via UCSI_CCI_CONNECTOR() is a 7-bit field (0-127) that is used to index into the connector array in ucsi_connector_change(). However, the array is only allocated for the number of connectors reported by the device (typically 2-4 entries). A malicious or malfunctioning device could report an out-of-range connector number in the CCI, causing an out-of-bounds array access in ucsi_connector_change(). Add a bounds check in ucsi_notify_common(), the central point where CCI is parsed after arriving from hardware, so that bogus connector numbers are rejected before they propagate further. Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable Reviewed-by: Heikki Krogerus Signed-off-by: Nathan Rebello Link: https://patch.msgid.link/20260313222453.123-1-nathan.c.rebello@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f38a4d7ebc4243..8333bdaf55668d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -43,8 +43,13 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) if (cci & UCSI_CCI_BUSY) return; - if (UCSI_CCI_CONNECTOR(cci)) - ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + if (UCSI_CCI_CONNECTOR(cci)) { + if (UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors) + ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + else + dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n", + UCSI_CCI_CONNECTOR(cci)); + } if (cci & UCSI_CCI_ACK_COMPLETE && test_and_clear_bit(ACK_PENDING, &ucsi->flags)) From 616a63ff495df12863692ab3f9f7b84e3fa7a66d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 15 Mar 2026 14:30:43 -0400 Subject: [PATCH 071/700] USB: dummy-hcd: Fix locking/synchronization error Syzbot testing was able to provoke an addressing exception and crash in the usb_gadget_udc_reset() routine in drivers/usb/gadgets/udc/core.c, resulting from the fact that the routine was called with a second ("driver") argument of NULL. The bad caller was set_link_state() in dummy_hcd.c, and the problem arose because of a race between a USB reset and driver unbind. These sorts of races were not supposed to be possible; commit 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change"), along with a few followup commits, was written specifically to prevent them. As it turns out, there are (at least) two errors remaining in the code. Another patch will address the second error; this one is concerned with the first. The error responsible for the syzbot crash occurred because the stop_activity() routine will sometimes drop and then re-acquire the dum->lock spinlock. A call to stop_activity() occurs in set_link_state() when handling an emulated USB reset, after the test of dum->ints_enabled and before the increment of dum->callback_usage. This allowed another thread (doing a driver unbind) to sneak in and grab the spinlock, and then clear dum->ints_enabled and dum->driver. Normally this other thread would have to wait for dum->callback_usage to go down to 0 before it would clear dum->driver, but in this case it didn't have to wait since dum->callback_usage had not yet been incremented. The fix is to increment dum->callback_usage _before_ calling stop_activity() instead of after. Then the thread doing the unbind will not clear dum->driver until after the call to usb_gadget_udc_reset() safely returns and dum->callback_usage has been decremented again. Signed-off-by: Alan Stern Reported-by: syzbot+19bed92c97bee999e5db@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-usb/68fc7c9c.050a0220.346f24.023c.GAE@google.com/ Tested-by: syzbot+19bed92c97bee999e5db@syzkaller.appspotmail.com Fixes: 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change") Cc: stable Link: https://patch.msgid.link/46135f42-fdbe-46b5-aac0-6ca70492af15@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index c9eca90376e2b0..860ee7d3ef982f 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -462,8 +462,13 @@ static void set_link_state(struct dummy_hcd *dum_hcd) /* Report reset and disconnect events to the driver */ if (dum->ints_enabled && (disconnect || reset)) { - stop_activity(dum); ++dum->callback_usage; + /* + * stop_activity() can drop dum->lock, so it must + * not come between the dum->ints_enabled test + * and the ++dum->callback_usage. + */ + stop_activity(dum); spin_unlock(&dum->lock); if (reset) usb_gadget_udc_reset(&dum->gadget, dum->driver); From 2ca9e46f8f1f5a297eb0ac83f79d35d5b3a02541 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 15 Mar 2026 14:31:00 -0400 Subject: [PATCH 072/700] USB: dummy-hcd: Fix interrupt synchronization error This fixes an error in synchronization in the dummy-hcd driver. The error has a somewhat involved history. The synchronization mechanism was introduced by commit 7dbd8f4cabd9 ("USB: dummy-hcd: Fix erroneous synchronization change"), which added an emulated "interrupts enabled" flag together with code emulating synchronize_irq() (it waits until all current handler callbacks have returned). But the emulated interrupt-disable occurred too late, after the driver containing the handler callback routines had been told that it was unbound and no more callbacks would occur. Commit 4a5d797a9f9c ("usb: gadget: dummy_hcd: fix gpf in gadget_setup") tried to fix this by moving the synchronize_irq() emulation code from dummy_stop() to dummy_pullup(), which runs before the unbind callback. There still were races, though, because the emulated interrupt-disable still occurred too late. It couldn't be moved to dummy_pullup(), because that routine can be called for reasons other than an impending unbind. Therefore commits 7dc0c55e9f30 ("USB: UDC core: Add udc_async_callbacks gadget op") and 04145a03db9d ("USB: UDC: Implement udc_async_callbacks in dummy-hcd") added an API allowing the UDC core to tell dummy-hcd exactly when emulated interrupts and their callbacks should be disabled. That brings us to the current state of things, which is still wrong because the emulated synchronize_irq() occurs before the emulated interrupt-disable! That's no good, beause it means that more emulated interrupts can occur after the synchronize_irq() emulation has run, leading to the possibility that a callback handler may be running when the gadget driver is unbound. To fix this, we have to move the synchronize_irq() emulation code yet again, to the dummy_udc_async_callbacks() routine, which takes care of enabling and disabling emulated interrupt requests. The synchronization will now run immediately after emulated interrupts are disabled, which is where it belongs. Signed-off-by: Alan Stern Fixes: 04145a03db9d ("USB: UDC: Implement udc_async_callbacks in dummy-hcd") Cc: stable Link: https://patch.msgid.link/c7bc93fe-4241-4d04-bd56-27c12ba35c97@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 860ee7d3ef982f..e55701575857ca 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -913,21 +913,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); set_link_state(dum_hcd); - if (value == 0) { - /* - * Emulate synchronize_irq(): wait for callbacks to finish. - * This seems to be the best place to emulate the call to - * synchronize_irq() that's in usb_gadget_remove_driver(). - * Doing it in dummy_udc_stop() would be too late since it - * is called after the unbind callback and unbind shouldn't - * be invoked until all the other callbacks are finished. - */ - while (dum->callback_usage > 0) { - spin_unlock_irqrestore(&dum->lock, flags); - usleep_range(1000, 2000); - spin_lock_irqsave(&dum->lock, flags); - } - } spin_unlock_irqrestore(&dum->lock, flags); usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); @@ -950,6 +935,20 @@ static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable) spin_lock_irq(&dum->lock); dum->ints_enabled = enable; + if (!enable) { + /* + * Emulate synchronize_irq(): wait for callbacks to finish. + * This has to happen after emulated interrupts are disabled + * (dum->ints_enabled is clear) and before the unbind callback, + * just like the call to synchronize_irq() in + * gadget/udc/core:gadget_unbind_driver(). + */ + while (dum->callback_usage > 0) { + spin_unlock_irq(&dum->lock); + usleep_range(1000, 2000); + spin_lock_irq(&dum->lock); + } + } spin_unlock_irq(&dum->lock); } From e002e92e88e12457373ed096b18716d97e7bbb20 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Mon, 16 Mar 2026 15:49:09 +0800 Subject: [PATCH 073/700] usb: gadget: u_ether: Fix NULL pointer deref in eth_get_drvinfo Commit ec35c1969650 ("usb: gadget: f_ncm: Fix net_device lifecycle with device_move") reparents the gadget device to /sys/devices/virtual during unbind, clearing the gadget pointer. If the userspace tool queries on the surviving interface during this detached window, this leads to a NULL pointer dereference. Unable to handle kernel NULL pointer dereference Call trace: eth_get_drvinfo+0x50/0x90 ethtool_get_drvinfo+0x5c/0x1f0 __dev_ethtool+0xaec/0x1fe0 dev_ethtool+0x134/0x2e0 dev_ioctl+0x338/0x560 Add a NULL check for dev->gadget in eth_get_drvinfo(). When detached, skip copying the fw_version and bus_info strings, which is natively handled by ethtool_get_drvinfo for empty strings. Suggested-by: Val Packett Reported-by: Val Packett Closes: https://lore.kernel.org/linux-usb/10890524-cf83-4a71-b879-93e2b2cc1fcc@packett.cool/ Fixes: ec35c1969650 ("usb: gadget: f_ncm: Fix net_device lifecycle with device_move") Cc: stable Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260316-eth-null-deref-v1-1-07005f33be85@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ether.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 23c7c0cdf20295..59d85d6a84a896 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -113,8 +113,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) strscpy(p->driver, "g_ether", sizeof(p->driver)); strscpy(p->version, UETH__VERSION, sizeof(p->version)); - strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); - strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + if (dev->gadget) { + strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + } } /* REVISIT can also support: From b2f6648c735639d246dc5f98f377b69d5374c2bd Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 16 Mar 2026 17:48:11 +0800 Subject: [PATCH 074/700] usb: hcd: queue wakeup_work to system_freezable_wq workqueue After commit 4fb352df14de ("PM: sleep: Do not flag runtime PM workqueue as freezable"), pm_wq workqueue will be unfreezable during system pm. This brings issue as below: [ 344.255749] ------------[ cut here ]------------ [ 344.277740] URB 000000004aae4ad1 submitted while active [ 344.282996] WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x5a4/0x5e0, CPU#2: kworker/u16:14/964 [ 344.292477] Modules linked in: [ 344.295532] CPU: 2 UID: 0 PID: 964 Comm: kworker/u16:14 Not tainted 7.0.0-rc2-next-20260303-00006-gf03fe0b53b39-dirty #100 PREEMPT [ 344.307341] Hardware name: NXP i.MX943 EVK board (DT) [ 344.312386] Workqueue: async async_run_entry_fn [ 344.316919] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 344.323862] pc : usb_submit_urb+0x5a4/0x5e0 [ 344.328046] lr : usb_submit_urb+0x5a4/0x5e0 [ 344.332217] sp : ffff800083283b30 [ 344.335528] x29: ffff800083283b30 x28: ffff000082631000 x27: 0000000000000000 [ 344.342661] x26: 0000000000000003 x25: 0000000000000c00 x24: 0000000000000000 [ 344.349793] x23: 0000000000000004 x22: 0000000000000010 x21: 0000000000000000 [ 344.356917] x20: 0000000000000002 x19: ffff00008253ce40 x18: ffff000089e4eec0 [ 344.364050] x17: 000000040044ffff x16: 000005d9d87f6289 x15: 0000000000000000 [ 344.371174] x14: ffff000089e4eec0 x13: 6576697463612065 x12: 6c69687720646574 [ 344.378298] x11: 0000000000000058 x10: 0000000000000001 x9 : 0000000000000001 [ 344.385431] x8 : 0000000000000000 x7 : 0000005028dd0800 x6 : 0000000000000002 [ 344.392563] x5 : ffffc48f74e9daf8 x4 : 0000000000000000 x3 : 0000000000000000 [ 344.399696] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000089e4ee40 [ 344.406835] Call trace: [ 344.409280] usb_submit_urb+0x5a4/0x5e0 (P) [ 344.413456] hub_activate+0x268/0x878 [ 344.417116] hub_resume+0x5c/0x10c [ 344.420522] usb_resume_interface.isra.0+0xa8/0x110 [ 344.425393] usb_resume_both+0x10c/0x1e0 [ 344.429310] usb_resume+0x1c/0x70 [ 344.432621] usb_dev_resume+0x14/0x20 [ 344.436278] dpm_run_callback.isra.0+0x38/0xf8 [ 344.440715] device_resume+0xec/0x1e8 [ 344.444373] async_resume+0x20/0x38 [ 344.447848] async_run_entry_fn+0x34/0xe0 [ 344.451852] process_one_work+0x150/0x290 [ 344.455856] worker_thread+0x18c/0x300 [ 344.459600] kthread+0x118/0x124 [ 344.462824] ret_from_fork+0x10/0x20 The reason is if the host controller resume routine xhci_resume() call usb_hcd_resume_root_hub(), wakeup_work will be queued and run immediately. Then usb_autoresume_device() will be called and usb device will exit runtime suspended state (if it was suspended before). For a hub device, hub_resume()/hub_reset_resume() will be called accordingly. After the host controller device system resume is finished, the root hub usb device "usb1" will do system resume too. Then hub_resume() will be called again. Above sequence will cause hub->urb to be submitted twice. To avoid this issue, restore the previous behavior by queuing wakeup_work to system_freezable_wq workqueue. Acked-by: Alan Stern Fixes: 4fb352df14de ("PM: sleep: Do not flag runtime PM workqueue as freezable") Cc: stable Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260316094811.1559471-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index dee842ea69311d..89221f1ce7694f 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2403,7 +2403,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) if (hcd->rh_registered) { pm_wakeup_event(&hcd->self.root_hub->dev, 0); set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); - queue_work(pm_wq, &hcd->wakeup_work); + queue_work(system_freezable_wq, &hcd->wakeup_work); } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } From f97e96c303d689708f7f713d8f3afcc31f1237e9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 17 Mar 2026 09:41:10 +0100 Subject: [PATCH 075/700] cdc-acm: new quirk for EPSON HMD This device has a union descriptor that is just garbage and needs a custom descriptor. In principle this could be done with a (conditionally activated) heuristic. That would match more devices without a need for defining a new quirk. However, this always carries the risk that the heuristics does the wrong thing and leads to more breakage. Defining the quirk and telling it exactly what to do is the safe and conservative approach. Signed-off-by: Oliver Neukum Cc: stable Link: https://patch.msgid.link/20260317084139.1461008-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 9 +++++++++ drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7ede29d4c7c134..cf3c3eede1a51f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1225,6 +1225,12 @@ static int acm_probe(struct usb_interface *intf, if (!data_interface || !control_interface) return -ENODEV; goto skip_normal_probe; + } else if (quirks == NO_UNION_12) { + data_interface = usb_ifnum_to_if(usb_dev, 2); + control_interface = usb_ifnum_to_if(usb_dev, 1); + if (!data_interface || !control_interface) + return -ENODEV; + goto skip_normal_probe; } /* normal probing*/ @@ -1748,6 +1754,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, + { USB_DEVICE(0x04b8, 0x0d12), /* EPSON HMD Com&Sens */ + .driver_info = NO_UNION_12, /* union descriptor is garbage */ + }, { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 76f73853a60b63..25fd5329a8781f 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -114,3 +114,4 @@ struct acm { #define SEND_ZERO_PACKET BIT(6) #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) +#define NO_UNION_12 BIT(9) From 1a122198ee26d2f328edae802b2ca4fa0518a20a Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 16 Mar 2026 23:30:06 +0800 Subject: [PATCH 076/700] dwc3: google: Fix PM domain leak in dwc3_google_probe() When syscon_regmap_lookup_by_phandle_args() fails, the function was returning directly without cleaning up the power domain initialized earlier by dwc3_google_pm_domain_init(). Fix by jumping to err_deinit_pdom to properly clean up resources. Fixes: 8995a37371bf4 ("usb: dwc3: Add Google Tensor SoC DWC3 glue driver") Signed-off-by: Felix Gu Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260316-dwc3-google-v1-1-c9bde1b02f62@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-google.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 2105c72af75327..4ca567ec01d071 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -385,8 +385,9 @@ static int dwc3_google_probe(struct platform_device *pdev) "google,usb-cfg-csr", ARRAY_SIZE(args), args); if (IS_ERR(google->usb_cfg_regmap)) { - return dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap), - "invalid usb cfg csr\n"); + ret = dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap), + "invalid usb cfg csr\n"); + goto err_deinit_pdom; } google->host_cfg_offset = args[0]; From f50200dd44125e445a6164e88c217472fa79cdbc Mon Sep 17 00:00:00 2001 From: Sebastian Urban Date: Sun, 15 Mar 2026 16:10:45 +0100 Subject: [PATCH 077/700] usb: gadget: dummy_hcd: fix premature URB completion when ZLP follows partial transfer When a gadget request is only partially transferred in transfer() because the per-frame bandwidth budget is exhausted, the loop advances to the next queued request. If that next request is a zero-length packet (ZLP), len evaluates to zero and the code takes the unlikely(len == 0) path, which sets is_short = 1. This bypasses the bandwidth guard ("limit < ep->ep.maxpacket && limit < len") that lives in the else branch and would otherwise break out of the loop for non-zero requests. The is_short path then completes the URB before all data from the first request has been transferred. Reproducer (bulk IN, high speed): Device side (FunctionFS with Linux AIO): 1. Queue a 65024-byte write via io_submit (127 * 512, i.e. a multiple of the HS bulk max packet size). 2. Immediately queue a zero-length write (ZLP) via io_submit. Host side: 3. Submit a 65536-byte bulk IN URB. Expected: URB completes with actual_length = 65024. Actual: URB completes with actual_length = 53248, losing 11776 bytes that leak into subsequent URBs. At high speed the per-frame budget is 53248 bytes (512 * 13 * 8). The 65024-byte request exhausts this budget after 53248 bytes, leaving the request incomplete (req->req.actual < req->req.length). Neither the request nor the URB is finished, and rescan is 0, so the loop advances to the ZLP. For the ZLP, dev_len = 0, so len = min(12288, 0) = 0, taking the unlikely(len == 0) path and setting is_short = 1. The is_short handler then sets *status = 0, completing the URB with only 53248 of the expected 65024 bytes. Fix this by breaking out of the loop when the current request has remaining data (req->req.actual < req->req.length). The request resumes on the next timer tick, preserving correct data ordering. Signed-off-by: Sebastian Urban Cc: stable Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260315151045.1155850-1-surban@surban.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index e55701575857ca..f094491b1041ae 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1538,6 +1538,12 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, /* rescan to continue with any other queued i/o */ if (rescan) goto top; + + /* request not fully transferred; stop iterating to + * preserve data ordering across queued requests. + */ + if (req->req.actual < req->req.length) + break; } return sent; } From cdbefe9d4029d4834d404f7ba13a960b38a69e88 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 11 Mar 2026 11:00:34 +0200 Subject: [PATCH 078/700] tools/power turbostat: Fix delimiter bug in print functions Commands that add counters, such as 'turbostat --show C1,C1+' display merged columns without a delimiter. This is caused by the bad syntax: '(*printed++ ? delim : "")', shared by print_name()/print_hex_value()/print_decimal_value()/print_float_value() Use '((*printed)++ ? delim : "")' to correctly increment the value at *printed. [lenb: fix code and commit message typo, re-word] Fixes: 56dbb878507b ("tools/power turbostat: Refactor added column header printing") Signed-off-by: Artem Bityutskiy Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 3487548841e1d7..34e2143cd4b317 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -2837,29 +2837,29 @@ static inline int print_name(int width, int *printed, char *delim, char *name, e UNUSED(type); if (format == FORMAT_RAW && width >= 64) - return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); + return (sprintf(outp, "%s%-8s", ((*printed)++ ? delim : ""), name)); else - return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); + return (sprintf(outp, "%s%s", ((*printed)++ ? delim : ""), name)); } static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) { if (width <= 32) - return (sprintf(outp, "%s%08x", (*printed++ ? delim : ""), (unsigned int)value)); + return (sprintf(outp, "%s%08x", ((*printed)++ ? delim : ""), (unsigned int)value)); else - return (sprintf(outp, "%s%016llx", (*printed++ ? delim : ""), value)); + return (sprintf(outp, "%s%016llx", ((*printed)++ ? delim : ""), value)); } static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) { UNUSED(width); - return (sprintf(outp, "%s%lld", (*printed++ ? delim : ""), value)); + return (sprintf(outp, "%s%lld", ((*printed)++ ? delim : ""), value)); } static inline int print_float_value(int *printed, char *delim, double value) { - return (sprintf(outp, "%s%0.2f", (*printed++ ? delim : ""), value)); + return (sprintf(outp, "%s%0.2f", ((*printed)++ ? delim : ""), value)); } void print_header(char *delim) From a343fb1e03cfc9f6dc83a5efb2a8d33e9cdaf6b9 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 17 Mar 2026 15:14:40 +0100 Subject: [PATCH 079/700] soc: qcom: pmic_glink_altmode: Fix TBT->SAFE->!TBT transition Similar to the case of commit d48708500610 ("soc: qcom: pmic_glink_altmode: Fix SVID=DP && unconnected edge case"), leaving the TBT altmode makes pmic_glink_altmode report a SVID=TBT && mux_ctrl=0 message. Said commit reordered the check such that the SVID is processed before checking for NO_CONN. Rework this to take into account valid values of mux_ctrl first and hopefully solve this for good.. Fixes: d48708500610 ("soc: qcom: pmic_glink_altmode: Fix SVID=DP && unconnected edge case") Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260317-topic-tbt_pg_fixup-v1-1-325b8647bc82@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index b496b88842a27e..619bad2c27eeb3 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -62,6 +62,9 @@ struct usbc_notify { u8 orientation; u8 mux_ctrl; #define MUX_CTRL_STATE_NO_CONN 0 +#define MUX_CTRL_STATE_USB3_ONLY 1 +#define MUX_CTRL_STATE_DP4LN 2 +#define MUX_CTRL_STATE_USB3_DP 3 #define MUX_CTRL_STATE_TUNNELING 4 u8 res; @@ -350,9 +353,12 @@ static void pmic_glink_altmode_worker(struct work_struct *work) typec_switch_set(alt_port->typec_switch, alt_port->orientation); - if (alt_port->svid == USB_TYPEC_TBT_SID) { - pmic_glink_altmode_enable_tbt(altmode, alt_port); - } else if (alt_port->svid == USB_TYPEC_DP_SID) { + /* + * MUX_CTRL_STATE_DP4LN/USB3_DP may only be set if SVID=DP, but we need + * to special-case the SVID=DP && mux_ctrl=NO_CONN case to deliver a + * HPD notification + */ + if (alt_port->svid == USB_TYPEC_DP_SID) { if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { pmic_glink_altmode_safe(altmode, alt_port); } else { @@ -369,11 +375,18 @@ static void pmic_glink_altmode_worker(struct work_struct *work) drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status); } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) { - pmic_glink_altmode_enable_usb4(altmode, alt_port); + if (alt_port->svid == USB_TYPEC_TBT_SID) + pmic_glink_altmode_enable_tbt(altmode, alt_port); + else + pmic_glink_altmode_enable_usb4(altmode, alt_port); + } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_USB3_ONLY) { + pmic_glink_altmode_enable_usb(altmode, alt_port); } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) { pmic_glink_altmode_safe(altmode, alt_port); } else { - pmic_glink_altmode_enable_usb(altmode, alt_port); + dev_err(altmode->dev, "Got unknown mux_ctrl: %u on port %u, forcing safe mode\n", + alt_port->mux_ctrl, alt_port->index); + pmic_glink_altmode_safe(altmode, alt_port); } pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index); From 340bba73c545bfc7e8fcbc5ee4c02f85088f024d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Mar 2026 20:30:25 +0100 Subject: [PATCH 080/700] pinctrl: intel: Improve capability support The register space of a certain capability starts at the offset just after the respective node in the capability list. It means that there are no fixed offsets for them from SoC to SoC generation and they have to be calculated at run-time. Improve capability support by adding the respective calculation algorithm and in the result enable PWM on more platforms that currently may use the wrong register. Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-intel.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 9d32bb8bc13a2b..adaa37a42754e6 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -53,8 +53,6 @@ #define PADOWN_MASK(p) (GENMASK(3, 0) << PADOWN_SHIFT(p)) #define PADOWN_GPP(p) ((p) / 8) -#define PWMC 0x204 - /* Offset from pad_regs */ #define PADCFG0 0x000 #define PADCFG0_RXEVCFG_MASK GENMASK(26, 25) @@ -1549,8 +1547,10 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) } static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl, - struct intel_community *community) + struct intel_community *community, + unsigned short capability_offset) { + void __iomem *base = community->regs + capability_offset + 4; static const struct pwm_lpss_boardinfo info = { .clk_rate = 19200000, .npwm = 1, @@ -1564,7 +1564,7 @@ static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl, if (!IS_REACHABLE(CONFIG_PWM_LPSS)) return 0; - chip = devm_pwm_lpss_probe(pctrl->dev, community->regs + PWMC, &info); + chip = devm_pwm_lpss_probe(pctrl->dev, base, &info); return PTR_ERR_OR_ZERO(chip); } @@ -1595,6 +1595,7 @@ int intel_pinctrl_probe(struct platform_device *pdev, for (i = 0; i < pctrl->ncommunities; i++) { struct intel_community *community = &pctrl->communities[i]; + unsigned short capability_offset[6]; void __iomem *regs; u32 offset; u32 value; @@ -1622,15 +1623,19 @@ int intel_pinctrl_probe(struct platform_device *pdev, switch ((value & CAPLIST_ID_MASK) >> CAPLIST_ID_SHIFT) { case CAPLIST_ID_GPIO_HW_INFO: community->features |= PINCTRL_FEATURE_GPIO_HW_INFO; + capability_offset[CAPLIST_ID_GPIO_HW_INFO] = offset; break; case CAPLIST_ID_PWM: community->features |= PINCTRL_FEATURE_PWM; + capability_offset[CAPLIST_ID_PWM] = offset; break; case CAPLIST_ID_BLINK: community->features |= PINCTRL_FEATURE_BLINK; + capability_offset[CAPLIST_ID_BLINK] = offset; break; case CAPLIST_ID_EXP: community->features |= PINCTRL_FEATURE_EXP; + capability_offset[CAPLIST_ID_EXP] = offset; break; default: break; @@ -1653,7 +1658,7 @@ int intel_pinctrl_probe(struct platform_device *pdev, if (ret) return ret; - ret = intel_pinctrl_probe_pwm(pctrl, community); + ret = intel_pinctrl_probe_pwm(pctrl, community, capability_offset[CAPLIST_ID_PWM]); if (ret) return ret; } From a4337a24d13e9e3b98a113e71d6b80dc5ed5f8c4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Mar 2026 18:14:04 +0100 Subject: [PATCH 081/700] pinctrl: intel: Fix the revision for new features (1kOhm PD, HW debouncer) The 1kOhm pull down and hardware debouncer are features of the revision 0.92 of the Chassis specification. Fix that in the code accordingly. Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index adaa37a42754e6..a5a264ba6fbb81 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -1611,7 +1611,7 @@ int intel_pinctrl_probe(struct platform_device *pdev, value = readl(regs + REVID); if (value == ~0u) return -ENODEV; - if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x94) { + if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x92) { community->features |= PINCTRL_FEATURE_DEBOUNCE; community->features |= PINCTRL_FEATURE_1K_PD; } From 1ca468e78ea97c3365befdd408f71bda4b295134 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Mar 2026 18:15:58 +0100 Subject: [PATCH 082/700] pinctrl: intel: Enable 3-bit PAD_OWN feature Starting from revision 1.1 of the Chassis specification the PAD_OWN is represented by 3 bits instead of 2 bits in the previous revisions. Update the driver to support this feature. Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-intel.c | 21 ++++++++++++++++----- drivers/pinctrl/intel/pinctrl-intel.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index a5a264ba6fbb81..97bf5ec78db4e0 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -203,19 +203,25 @@ static bool intel_pad_owned_by_host(const struct intel_pinctrl *pctrl, unsigned community = intel_get_community(pctrl, pin); if (!community) return false; - if (!community->padown_offset) + + /* If padown_offset is not provided, assume host ownership */ + padown = community->regs + community->padown_offset; + if (padown == community->regs) return true; + /* New HW generations have extended PAD_OWN registers */ + if (community->features & PINCTRL_FEATURE_3BIT_PAD_OWN) + return !(readl(padown + pin_to_padno(community, pin) * 4) & 7); + padgrp = intel_community_get_padgroup(community, pin); if (!padgrp) return false; gpp_offset = padgroup_offset(padgrp, pin); gpp = PADOWN_GPP(gpp_offset); - offset = community->padown_offset + padgrp->padown_num * 4 + gpp * 4; - padown = community->regs + offset; + offset = padgrp->padown_num * 4 + gpp * 4; - return !(readl(padown) & PADOWN_MASK(gpp_offset)); + return !(readl(padown + offset) & PADOWN_MASK(gpp_offset)); } static bool intel_pad_acpi_mode(const struct intel_pinctrl *pctrl, unsigned int pin) @@ -1597,6 +1603,7 @@ int intel_pinctrl_probe(struct platform_device *pdev, struct intel_community *community = &pctrl->communities[i]; unsigned short capability_offset[6]; void __iomem *regs; + u32 revision; u32 offset; u32 value; @@ -1611,10 +1618,14 @@ int intel_pinctrl_probe(struct platform_device *pdev, value = readl(regs + REVID); if (value == ~0u) return -ENODEV; - if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x92) { + + revision = (value & REVID_MASK) >> REVID_SHIFT; + if (revision >= 0x092) { community->features |= PINCTRL_FEATURE_DEBOUNCE; community->features |= PINCTRL_FEATURE_1K_PD; } + if (revision >= 0x110) + community->features |= PINCTRL_FEATURE_3BIT_PAD_OWN; /* Determine community features based on the capabilities */ offset = CAPLIST; diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h index 2f37109d5860ba..b5476b9de0dbf4 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.h +++ b/drivers/pinctrl/intel/pinctrl-intel.h @@ -150,6 +150,7 @@ struct intel_community { #define PINCTRL_FEATURE_PWM BIT(3) #define PINCTRL_FEATURE_BLINK BIT(4) #define PINCTRL_FEATURE_EXP BIT(5) +#define PINCTRL_FEATURE_3BIT_PAD_OWN BIT(6) #define __INTEL_COMMUNITY(b, s, e, g, n, gs, gn, soc) \ { \ From 27459f86a43792d5c29f267a41dbd387601e772b Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 9 Mar 2026 20:16:14 +0800 Subject: [PATCH 083/700] soc: microchip: mpfs-control-scb: Fix resource leak on driver unbind Use devm_mfd_add_devices() instead of mfd_add_devices() to ensure child devices are properly removed when the driver unbinds. Fixes: 4aac11c9a6e7 ("soc: microchip: add mfd drivers for two syscon regions on PolarFire SoC") Signed-off-by: Felix Gu Signed-off-by: Conor Dooley --- drivers/soc/microchip/mpfs-control-scb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/soc/microchip/mpfs-control-scb.c b/drivers/soc/microchip/mpfs-control-scb.c index f0b84b1f49cbca..8dda5704a389fb 100644 --- a/drivers/soc/microchip/mpfs-control-scb.c +++ b/drivers/soc/microchip/mpfs-control-scb.c @@ -14,8 +14,10 @@ static int mpfs_control_scb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - return mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_control_scb_devs, - ARRAY_SIZE(mpfs_control_scb_devs), NULL, 0, NULL); + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + mpfs_control_scb_devs, + ARRAY_SIZE(mpfs_control_scb_devs), NULL, 0, + NULL); } static const struct of_device_id mpfs_control_scb_of_match[] = { From 3bfc213d4675736567a4e263c51c25144d565949 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 9 Mar 2026 20:16:15 +0800 Subject: [PATCH 084/700] soc: microchip: mpfs-mss-top-sysreg: Fix resource leak on driver unbind Use devm_mfd_add_devices() instead of mfd_add_devices() to ensure child devices are properly removed when the driver unbinds. Fixes: 4aac11c9a6e7 ("soc: microchip: add mfd drivers for two syscon regions on PolarFire SoC") Signed-off-by: Felix Gu Signed-off-by: Conor Dooley --- drivers/soc/microchip/mpfs-mss-top-sysreg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/soc/microchip/mpfs-mss-top-sysreg.c b/drivers/soc/microchip/mpfs-mss-top-sysreg.c index b2244e44ff0fa7..b0f42b8dd3ed63 100644 --- a/drivers/soc/microchip/mpfs-mss-top-sysreg.c +++ b/drivers/soc/microchip/mpfs-mss-top-sysreg.c @@ -16,8 +16,10 @@ static int mpfs_mss_top_sysreg_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_mss_top_sysreg_devs, - ARRAY_SIZE(mpfs_mss_top_sysreg_devs) , NULL, 0, NULL); + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + mpfs_mss_top_sysreg_devs, + ARRAY_SIZE(mpfs_mss_top_sysreg_devs), NULL, + 0, NULL); if (ret) return ret; From c961cc86af01246a7ce706bbc29072d314e00880 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 18 Mar 2026 12:08:52 +0000 Subject: [PATCH 085/700] reset: rzg2l-usbphy-ctrl: Fix malformed MODULE_AUTHOR string Fix a malformed MODULE_AUTHOR macro in the RZ/G2L USBPHY control driver where the author's name and opening angle bracket were missing, leaving only the email address with a stray closing >. Correct it to the standard Name format. Signed-off-by: Biju Das Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzg2l-usbphy-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 05dd9b4a02df5c..fd75d9601a3bfd 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -350,4 +350,4 @@ module_platform_driver(rzg2l_usbphy_ctrl_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas RZ/G2L USBPHY Control"); -MODULE_AUTHOR("biju.das.jz@bp.renesas.com>"); +MODULE_AUTHOR("Biju Das "); From c1f2b0f2b5e37b2c27540a175aea2755a3799433 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 27 Feb 2026 15:19:58 +0800 Subject: [PATCH 086/700] arm64: dts: hisilicon: poplar: Correct PCIe reset GPIO polarity The PCIe reset GPIO on Poplar is actually active low. The active high worked before because kernel driver didn't respect the setting from DT. This is changed since commit 1d26a55fbeb9 ("PCI: histb: Switch to using gpiod API"), and thus PCIe on Poplar got brken since then. Fix the problem by correcting the polarity. Fixes: 32fa01761bd9 ("arm64: dts: hi3798cv200: enable PCIe support for poplar board") Cc: stable@vger.kernel.org Signed-off-by: Shawn Guo Signed-off-by: Wei Xu --- arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts index 7d370dac4c8571..579d55daa7d04e 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts @@ -179,7 +179,7 @@ }; &pcie { - reset-gpios = <&gpio4 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio4 4 GPIO_ACTIVE_LOW>; vpcie-supply = <®_pcie>; status = "okay"; }; From 1af997cad473d505248df6d9577183bb91f69670 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 27 Feb 2026 15:22:10 +0800 Subject: [PATCH 087/700] arm64: dts: hisilicon: hi3798cv200: Add missing dma-ranges Reboot starts failing on Poplar since commit 8424ecdde7df ("arm64: mm: Set ZONE_DMA size based on devicetree's dma-ranges"), which effectively changes zone_dma_bits from 30 to 32 for arm64 platforms that do not properly define dma-ranges in device tree. It's unclear how Poplar reboot gets broken by this change exactly, but a dma-ranges limiting zone_dma to the first 1 GB fixes the regression. Fixes: 2f20182ed670 ("arm64: dts: hisilicon: add dts files for hi3798cv200-poplar board") Cc: stable@vger.kernel.org Signed-off-by: Shawn Guo Signed-off-by: Wei Xu --- arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi b/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi index f6bc001c383263..2f4ad5da5e33c7 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi @@ -122,6 +122,7 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x0 0xf0000000 0x10000000>; + dma-ranges = <0x0 0x0 0x0 0x40000000>; crg: clock-reset-controller@8a22000 { compatible = "hisilicon,hi3798cv200-crg", "syscon", "simple-mfd"; From 24869650dff34a6fc8fd1cc91b2058a72f9abc95 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 18:13:31 -0500 Subject: [PATCH 088/700] iio: adc: ti-adc161s626: fix buffer read on big-endian Rework ti_adc_trigger_handler() to properly handle data on big-endian architectures. The scan data format is 16-bit CPU-endian, so we can't cast it to a int * on big-endian and expect it to work. Instead, we introduce a local int variable to read the data into, and then copy it to the buffer. Since the buffer isn't passed to any SPI functions, we don't need it to be DMA-safe. So we can drop it from the driver data struct and just use stack memory for the scan data. Since there is only one data value (plus timestamp), we don't need an array and can just declare a struct with the correct data type instead. Also fix alignment of iio_get_time_ns() to ( while we are touching this. Fixes: 4d671b71beef ("iio: adc: ti-adc161s626: add support for TI 1-channel differential ADCs") Signed-off-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc161s626.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 28aa6b80160c8b..42968d96572b4c 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -70,8 +70,6 @@ struct ti_adc_data { u8 read_size; u8 shift; - - u8 buffer[16] __aligned(IIO_DMA_MINALIGN); }; static int ti_adc_read_measurement(struct ti_adc_data *data, @@ -114,15 +112,20 @@ static irqreturn_t ti_adc_trigger_handler(int irq, void *private) struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; struct ti_adc_data *data = iio_priv(indio_dev); - int ret; + struct { + s16 data; + aligned_s64 timestamp; + } scan = { }; + int ret, val; + + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], &val); + if (ret) + goto exit_notify_done; - ret = ti_adc_read_measurement(data, &indio_dev->channels[0], - (int *) &data->buffer); - if (!ret) - iio_push_to_buffers_with_timestamp(indio_dev, - data->buffer, - iio_get_time_ns(indio_dev)); + scan.data = val; + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); + exit_notify_done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; From 768461517a28d80fe81ea4d5d03a90cd184ea6ad Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 18:13:32 -0500 Subject: [PATCH 089/700] iio: adc: ti-adc161s626: use DMA-safe memory for spi_read() Add a DMA-safe buffer and use it for spi_read() instead of a stack memory. All SPI buffers must be DMA-safe. Since we only need up to 3 bytes, we just use a u8[] instead of __be16 and __be32 and change the conversion functions appropriately. Fixes: 4d671b71beef ("iio: adc: ti-adc161s626: add support for TI 1-channel differential ADCs") Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc161s626.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 42968d96572b4c..be1cc2e77862b8 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ struct ti_adc_data { u8 read_size; u8 shift; + u8 buf[3] __aligned(IIO_DMA_MINALIGN); }; static int ti_adc_read_measurement(struct ti_adc_data *data, @@ -78,26 +80,20 @@ static int ti_adc_read_measurement(struct ti_adc_data *data, int ret; switch (data->read_size) { - case 2: { - __be16 buf; - - ret = spi_read(data->spi, (void *) &buf, 2); + case 2: + ret = spi_read(data->spi, data->buf, 2); if (ret) return ret; - *val = be16_to_cpu(buf); + *val = get_unaligned_be16(data->buf); break; - } - case 3: { - __be32 buf; - - ret = spi_read(data->spi, (void *) &buf, 3); + case 3: + ret = spi_read(data->spi, data->buf, 3); if (ret) return ret; - *val = be32_to_cpu(buf) >> 8; + *val = get_unaligned_be24(data->buf); break; - } default: return -EINVAL; } From fdc7aa54a5d44c05880a4aad7cfb41aacfd16d7b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 17:18:10 -0500 Subject: [PATCH 090/700] iio: light: vcnl4035: fix scan buffer on big-endian Rework vcnl4035_trigger_consumer_handler() so that we are not passing what should be a u16 value as an int * to regmap_read(). This won't work on bit endian systems. Instead, add a new unsigned int variable to pass to regmap_read(). Then copy that value into the buffer struct. The buffer array is replaced with a struct since there is only one value being read. This allows us to use the correct u16 data type and has a side-effect of simplifying the alignment specification. Also fix the endianness of the scan format from little-endian to CPU endianness. Since we are using regmap to read the value, it will be CPU-endian. Fixes: 55707294c4eb ("iio: light: Add support for vishay vcnl4035") Signed-off-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4035.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 963747927425f7..16aeb17067bc0c 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -103,17 +103,23 @@ static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct vcnl4035_data *data = iio_priv(indio_dev); /* Ensure naturally aligned timestamp */ - u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8) = { }; + struct { + u16 als_data; + aligned_s64 timestamp; + } buffer = { }; + unsigned int val; int ret; - ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer); + ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, &val); if (ret < 0) { dev_err(&data->client->dev, "Trigger consumer can't read from sensor.\n"); goto fail_read; } - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); + + buffer.als_data = val; + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, + iio_get_time_ns(indio_dev)); fail_read: iio_trigger_notify_done(indio_dev->trig); @@ -381,7 +387,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = { .sign = 'u', .realbits = 16, .storagebits = 16, - .endianness = IIO_LE, + .endianness = IIO_CPU, }, }, { @@ -395,7 +401,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = { .sign = 'u', .realbits = 16, .storagebits = 16, - .endianness = IIO_LE, + .endianness = IIO_CPU, }, }, }; From db08b1940f4beb25460b4a4e9da3446454f2e8fe Mon Sep 17 00:00:00 2001 From: Cheng-Yang Chou Date: Sat, 21 Mar 2026 18:54:58 +0800 Subject: [PATCH 091/700] sched_ext: Fix inconsistent NUMA node lookup in scx_select_cpu_dfl() In the WAKE_SYNC path of scx_select_cpu_dfl(), waker_node was computed with cpu_to_node(), while node (for prev_cpu) was computed with scx_cpu_node_if_enabled(). When scx_builtin_idle_per_node is disabled, idle_cpumask(waker_node) is called with a real node ID even though per-node idle tracking is disabled, resulting in undefined behavior. Fix by using scx_cpu_node_if_enabled() for waker_node as well, ensuring both variables are computed consistently. Fixes: 48849271e6611 ("sched_ext: idle: Per-node idle cpumasks") Cc: stable@vger.kernel.org # v6.15+ Signed-off-by: Cheng-Yang Chou Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext_idle.c index ba298ac3ce6cc1..0ae93cd6400467 100644 --- a/kernel/sched/ext_idle.c +++ b/kernel/sched/ext_idle.c @@ -543,7 +543,7 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags, * piled up on it even if there is an idle core elsewhere on * the system. */ - waker_node = cpu_to_node(cpu); + waker_node = scx_cpu_node_if_enabled(cpu); if (!(current->flags & PF_EXITING) && cpu_rq(cpu)->scx.local_dsq.nr == 0 && (!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) && From c7f27a8ab9f2f43570f0725256597a0d7abe2c5b Mon Sep 17 00:00:00 2001 From: Song Liu Date: Sat, 21 Mar 2026 20:30:45 -0700 Subject: [PATCH 092/700] workqueue: Fix false positive stall reports On weakly ordered architectures (e.g., arm64), the lockless check in wq_watchdog_timer_fn() can observe a reordering between the worklist insertion and the last_progress_ts update. Specifically, the watchdog can see a non-empty worklist (from a list_add) while reading a stale last_progress_ts value, causing a false positive stall report. This was confirmed by reading pool->last_progress_ts again after holding pool->lock in wq_watchdog_timer_fn(): workqueue watchdog: pool 7 false positive detected! lockless_ts=4784580465 locked_ts=4785033728 diff=453263ms worklist_empty=0 To avoid slowing down the hot path (queue_work, etc.), recheck last_progress_ts with pool->lock held. This will eliminate the false positive with minimal overhead. Remove two extra empty lines in wq_watchdog_timer_fn() as we are on it. Fixes: 82607adcf9cd ("workqueue: implement lockup detector") Cc: stable@vger.kernel.org # v4.5+ Assisted-by: claude-code:claude-opus-4-6 Signed-off-by: Song Liu Signed-off-by: Tejun Heo --- kernel/workqueue.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b77119d71641ae..ff97b705f25ed1 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -7699,8 +7699,28 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) else ts = touched; - /* did we stall? */ + /* + * Did we stall? + * + * Do a lockless check first. On weakly ordered + * architectures, the lockless check can observe a + * reordering between worklist insert_work() and + * last_progress_ts update from __queue_work(). Since + * __queue_work() is a much hotter path than the timer + * function, we handle false positive here by reading + * last_progress_ts again with pool->lock held. + */ if (time_after(now, ts + thresh)) { + scoped_guard(raw_spinlock_irqsave, &pool->lock) { + pool_ts = pool->last_progress_ts; + if (time_after(pool_ts, touched)) + ts = pool_ts; + else + ts = touched; + } + if (!time_after(now, ts + thresh)) + continue; + lockup_detected = true; stall_time = jiffies_to_msecs(now - pool_ts) / 1000; max_stall_time = max(max_stall_time, stall_time); @@ -7712,8 +7732,6 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) pr_cont_pool_info(pool); pr_cont(" stuck for %us!\n", stall_time); } - - } if (lockup_detected) From 67c3f99bed6f422ba343d2b70a2eeeccdfd91bef Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Fri, 30 Jan 2026 14:23:52 +0200 Subject: [PATCH 093/700] counter: rz-mtu3-cnt: prevent counter from being toggled multiple times Runtime PM counter is incremented / decremented each time the sysfs enable file is written to. If user writes 0 to the sysfs enable file multiple times, runtime PM usage count underflows, generating the following message. rz-mtu3-counter rz-mtu3-counter.0: Runtime PM usage count underflow! At the same time, hardware registers end up being accessed with clocks off in rz_mtu3_terminate_counter() to disable an already disabled channel. If user writes 1 to the sysfs enable file multiple times, runtime PM usage count will be incremented each time, requiring the same number of 0 writes to get it back to 0. If user writes 0 to the sysfs enable file while PWM is in progress, PWM is stopped without counter being the owner of the underlying MTU3 channel. Check against the cached count_is_enabled value and exit if the user is trying to set the same enable value. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20260130122353.2263273-5-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: William Breathitt Gray --- drivers/counter/rz-mtu3-cnt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index e755d54dfece94..a4a8ef2d88f064 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -499,21 +499,25 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter, struct rz_mtu3_cnt *const priv = counter_priv(counter); int ret = 0; + mutex_lock(&priv->lock); + + if (priv->count_is_enabled[count->id] == enable) + goto exit; + if (enable) { - mutex_lock(&priv->lock); pm_runtime_get_sync(ch->dev); ret = rz_mtu3_initialize_counter(counter, count->id); if (ret == 0) priv->count_is_enabled[count->id] = true; - mutex_unlock(&priv->lock); } else { - mutex_lock(&priv->lock); rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] = false; pm_runtime_put(ch->dev); - mutex_unlock(&priv->lock); } +exit: + mutex_unlock(&priv->lock); + return ret; } From 2932095c114b98cbb40ccf34fc00d613cb17cead Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Fri, 30 Jan 2026 14:23:53 +0200 Subject: [PATCH 094/700] counter: rz-mtu3-cnt: do not use struct rz_mtu3_channel's dev member The counter driver can use HW channels 1 and 2, while the PWM driver can use HW channels 0, 1, 2, 3, 4, 6, 7. The dev member is assigned both by the counter driver and the PWM driver for channels 1 and 2, to their own struct device instance, overwriting the previous value. The sub-drivers race to assign their own struct device pointer to the same struct rz_mtu3_channel's dev member. The dev member of struct rz_mtu3_channel is used by the counter sub-driver for runtime PM. Depending on the probe order of the counter and PWM sub-drivers, the dev member may point to the wrong struct device instance, causing the counter sub-driver to do runtime PM actions on the wrong device. To fix this, use the parent pointer of the counter, which is assigned during probe to the correct struct device, not the struct device pointer inside the shared struct rz_mtu3_channel. Cc: stable@vger.kernel.org Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver") Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20260130122353.2263273-6-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: William Breathitt Gray --- drivers/counter/rz-mtu3-cnt.c | 55 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index a4a8ef2d88f064..7bfb6979193ce6 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -107,9 +107,9 @@ static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id) struct rz_mtu3_cnt *const priv = counter_priv(counter); unsigned long tmdr; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr)) return false; @@ -165,12 +165,12 @@ static int rz_mtu3_count_read(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) *val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW); else *val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -187,26 +187,26 @@ static int rz_mtu3_count_write(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; } static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch, - struct rz_mtu3_cnt *const priv, + struct counter_device *const counter, enum counter_function *function) { u8 timer_mode; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) { case RZ_MTU3_TMDR1_PH_CNT_MODE_1: @@ -240,7 +240,7 @@ static int rz_mtu3_count_function_read(struct counter_device *counter, if (ret) return ret; - ret = rz_mtu3_count_function_read_helper(ch, priv, function); + ret = rz_mtu3_count_function_read_helper(ch, counter, function); mutex_unlock(&priv->lock); return ret; @@ -279,9 +279,9 @@ static int rz_mtu3_count_function_write(struct counter_device *counter, return -EINVAL; } - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -300,9 +300,9 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); *direction = (tsr & RZ_MTU3_TSR_TCFD) ? COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; @@ -377,14 +377,14 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter, return -EINVAL; } - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); if (count->id == RZ_MTU3_32_BIT_CH) rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling); else rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling); rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA); - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -495,7 +495,6 @@ static int rz_mtu3_count_enable_read(struct counter_device *counter, static int rz_mtu3_count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id); struct rz_mtu3_cnt *const priv = counter_priv(counter); int ret = 0; @@ -505,14 +504,14 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter, goto exit; if (enable) { - pm_runtime_get_sync(ch->dev); + pm_runtime_get_sync(counter->parent); ret = rz_mtu3_initialize_counter(counter, count->id); if (ret == 0) priv->count_is_enabled[count->id] = true; } else { rz_mtu3_terminate_counter(counter, count->id); priv->count_is_enabled[count->id] = false; - pm_runtime_put(ch->dev); + pm_runtime_put(counter->parent); } exit: @@ -544,9 +543,9 @@ static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr); mutex_unlock(&priv->lock); @@ -563,10 +562,10 @@ static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter, if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_LWA, cascade_enable); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -583,9 +582,9 @@ static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *count if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr); mutex_unlock(&priv->lock); @@ -602,11 +601,11 @@ static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *count if (ret) return ret; - pm_runtime_get_sync(priv->ch->dev); + pm_runtime_get_sync(counter->parent); rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_PHCKSEL, ext_input_phase_clock_select); - pm_runtime_put(priv->ch->dev); + pm_runtime_put(counter->parent); mutex_unlock(&priv->lock); return 0; @@ -644,7 +643,7 @@ static int rz_mtu3_action_read(struct counter_device *counter, if (ret) return ret; - ret = rz_mtu3_count_function_read_helper(ch, priv, &function); + ret = rz_mtu3_count_function_read_helper(ch, counter, &function); if (ret) { mutex_unlock(&priv->lock); return ret; From 4f51e6c0baae80e52bd013092e82a55678be31fc Mon Sep 17 00:00:00 2001 From: Valek Andrej Date: Fri, 13 Mar 2026 10:24:13 +0100 Subject: [PATCH 095/700] iio: accel: fix ADXL355 temperature signature value Temperature was wrongly represented as 12-bit signed, confirmed by checking the datasheet. Even if the temperature is negative, the value in the register stays unsigned. Fixes: 12ed27863ea3 iio: accel: Add driver support for ADXL355 Signed-off-by: Valek Andrej Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 1c1d64d5cbcb20..8f90c58f41008a 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -745,7 +745,7 @@ static const struct iio_chan_spec adxl355_channels[] = { BIT(IIO_CHAN_INFO_OFFSET), .scan_index = 3, .scan_type = { - .sign = 's', + .sign = 'u', .realbits = 12, .storagebits = 16, .endianness = IIO_BE, From c354521708175d776d896f8bdae44b18711eccb6 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 12 Mar 2026 13:20:24 +0200 Subject: [PATCH 096/700] iio: dac: ad5770r: fix error return in ad5770r_read_raw() Return the error code from regmap_bulk_read() instead of 0 so that I/O failures are properly propagated. Fixes: cbbb819837f6 ("iio: dac: ad5770r: Add AD5770R support") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5770r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index cd47cb1c685c4d..6027e8d88b2781 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -322,7 +322,7 @@ static int ad5770r_read_raw(struct iio_dev *indio_dev, chan->address, st->transf_buf, 2); if (ret) - return 0; + return ret; buf16 = get_unaligned_le16(st->transf_buf); *val = buf16 >> 2; From 9d3fa23d5d55a137fd4396d3d4799102587a7f2b Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 12 Mar 2026 13:20:23 +0200 Subject: [PATCH 097/700] iio: accel: adxl313: add missing error check in predisable Check the return value of the FIFO bypass regmap_write() before proceeding to disable interrupts. Fixes: ff8093fa6ba4 ("iio: accel: adxl313: add buffered FIFO watermark with interrupt handling") Signed-off-by: Antoniu Miclaus Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 9f5d4d2cb325b3..83dcac17a0425f 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -998,6 +998,8 @@ static int adxl313_buffer_predisable(struct iio_dev *indio_dev) ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL, FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_BYPASS)); + if (ret) + return ret; ret = regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0); if (ret) From 2452969ca1081fea6bd9ab7ad5e168a5d11f28ec Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 22 Feb 2026 17:45:39 +0800 Subject: [PATCH 098/700] iio: adc: nxp-sar-adc: Fix DMA channel leak in trigger mode The DMA channel was requested in nxp_sar_adc_buffer_postenable() but was only released in nxp_sar_adc_buffer_software_do_predisable(). This caused a DMA channel resource leak when operating in trigger mode. Fix this by moving dma_request_chan() from nxp_sar_adc_buffer_postenable() into nxp_sar_adc_buffer_software_do_postenable(), ensuring the DMA channel is only requested in software mode. Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Signed-off-by: Felix Gu Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9efa883c277d23..58103bf16affb4 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -718,6 +718,10 @@ static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev) struct nxp_sar_adc *info = iio_priv(indio_dev); int ret; + info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx"); + if (IS_ERR(info->dma_chan)) + return PTR_ERR(info->dma_chan); + nxp_sar_adc_dma_channels_enable(info, *indio_dev->active_scan_mask); nxp_sar_adc_dma_cfg(info, true); @@ -738,6 +742,7 @@ static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev) out_dma_channels_disable: nxp_sar_adc_dma_cfg(info, false); nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask); + dma_release_channel(info->dma_chan); return ret; } @@ -765,10 +770,6 @@ static int nxp_sar_adc_buffer_postenable(struct iio_dev *indio_dev) unsigned long channel; int ret; - info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx"); - if (IS_ERR(info->dma_chan)) - return PTR_ERR(info->dma_chan); - info->channels_used = 0; /* From 710abda58055ed5eaa8958107633cc12a365c328 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Mar 2026 15:00:53 +0100 Subject: [PATCH 099/700] gpio: shared: call gpio_chip::of_xlate() if set OF-based GPIO controller drivers may provide a translation function that calculates the real chip offset from whatever devicetree sources provide. We need to take this into account in the shared GPIO management and call of_xlate() if it's provided and adjust the entry->offset we initially set when scanning the tree. To that end: modify the shared GPIO API to take the GPIO chip as argument on setup (to avoid having to rcu_dereference() it from the GPIO device) and protect the access to entry->offset with the existing lock. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Reported-by: Jon Hunter Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@nvidia.com/ Reviewed-by: Linus Walleij Tested-by: Jon Hunter Acked-by: Jon Hunter Link: https://patch.msgid.link/20260318-gpio-shared-xlate-v2-1-0ce34c707e81@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 27 ++++++++++++++++++++++++++- drivers/gpio/gpiolib-shared.h | 4 ++-- drivers/gpio/gpiolib.c | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 17a7128b6bd9bf..3a8db9bf456daa 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -506,8 +506,9 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev) auxiliary_device_uninit(adev); } -int gpio_device_setup_shared(struct gpio_device *gdev) +int gpiochip_setup_shared(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; struct gpio_shared_entry *entry; struct gpio_shared_ref *ref; struct gpio_desc *desc; @@ -532,12 +533,34 @@ int gpio_device_setup_shared(struct gpio_device *gdev) * exposing shared pins. Find them and create the proxy devices. */ list_for_each_entry(entry, &gpio_shared_list, list) { + guard(mutex)(&entry->lock); + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; if (list_count_nodes(&entry->refs) <= 1) continue; +#if IS_ENABLED(CONFIG_OF) + if (is_of_node(entry->fwnode) && gc->of_xlate) { + /* + * This is the earliest that we can tranlate the + * devicetree offset to the chip offset. + */ + struct of_phandle_args gpiospec = { }; + + gpiospec.np = to_of_node(entry->fwnode); + gpiospec.args_count = 2; + gpiospec.args[0] = entry->offset; + + ret = gc->of_xlate(gc, &gpiospec, NULL); + if (ret < 0) + return ret; + + entry->offset = ret; + } +#endif /* CONFIG_OF */ + desc = &gdev->descs[entry->offset]; __set_bit(GPIOD_FLAG_SHARED, &desc->flags); @@ -575,6 +598,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) struct gpio_shared_ref *ref; list_for_each_entry(entry, &gpio_shared_list, list) { + guard(mutex)(&entry->lock); + if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 40568ef7364ccb..e11e260e1f590c 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -14,14 +14,14 @@ struct device; #if IS_ENABLED(CONFIG_GPIO_SHARED) -int gpio_device_setup_shared(struct gpio_device *gdev); +int gpiochip_setup_shared(struct gpio_chip *gc); void gpio_device_teardown_shared(struct gpio_device *gdev); int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, unsigned long lflags); #else -static inline int gpio_device_setup_shared(struct gpio_device *gdev) +static inline int gpiochip_setup_shared(struct gpio_chip *gc) { return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f52d2d3efac4f0..d82fcf3fb45863 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1211,7 +1211,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_remove_irqchip_mask; - ret = gpio_device_setup_shared(gdev); + ret = gpiochip_setup_shared(gc); if (ret) goto err_remove_irqchip; From ec42a3a90ae9ae64b16d01a2e5d32ec0865ca8cf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Mar 2026 15:00:54 +0100 Subject: [PATCH 100/700] gpio: shared: handle pins shared by child nodes of devices Shared GPIOs may be assigned to child nodes of device nodes which don't themselves bind to any struct device. We need to pass the firmware node that is the actual consumer to gpiolib-shared and compare against it instead of unconditionally using the fwnode of the consumer device. Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support") Reported-by: Jon Hunter Closes: https://lore.kernel.org/all/921ba8ce-b18e-4a99-966d-c763d22081e2@nvidia.com/ Tested-by: Jon Hunter Acked-by: Jon Hunter Link: https://patch.msgid.link/20260318-gpio-shared-xlate-v2-2-0ce34c707e81@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 6 +++--- drivers/gpio/gpiolib-shared.h | 7 +++++-- drivers/gpio/gpiolib.c | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 3a8db9bf456daa..e257212fa5e3df 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -443,8 +443,8 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, } #endif /* CONFIG_RESET_GPIO */ -int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, - unsigned long lflags) +int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode, + const char *con_id, unsigned long lflags) { const char *dev_id = dev_name(consumer); struct gpiod_lookup_table *lookup; @@ -458,7 +458,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) { if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) continue; - } else if (!device_match_fwnode(consumer, ref->fwnode)) { + } else if (fwnode != ref->fwnode) { continue; } diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index e11e260e1f590c..15e72a8dcdb138 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -11,13 +11,15 @@ struct gpio_device; struct gpio_desc; struct device; +struct fwnode_handle; #if IS_ENABLED(CONFIG_GPIO_SHARED) int gpiochip_setup_shared(struct gpio_chip *gc); void gpio_device_teardown_shared(struct gpio_device *gdev); -int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, - unsigned long lflags); +int gpio_shared_add_proxy_lookup(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, unsigned long lflags); #else @@ -29,6 +31,7 @@ static inline int gpiochip_setup_shared(struct gpio_chip *gc) static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { } static inline int gpio_shared_add_proxy_lookup(struct device *consumer, + struct fwnode_handle *fwnode, const char *con_id, unsigned long lflags) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d82fcf3fb45863..300de30fd92021 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4714,8 +4714,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, * lookup table for the proxy device as previously * we only knew the consumer's fwnode. */ - ret = gpio_shared_add_proxy_lookup(consumer, con_id, - lookupflags); + ret = gpio_shared_add_proxy_lookup(consumer, fwnode, + con_id, lookupflags); if (ret) return ERR_PTR(ret); From 8de4e0f44c638c66cdc5eeb4d5ab9acd61c31e4f Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 20 Mar 2026 22:56:38 +0800 Subject: [PATCH 101/700] gpio: qixis-fpga: Fix error handling for devm_regmap_init_mmio() devm_regmap_init_mmio() returns an ERR_PTR() on failure, not NULL. The original code checked for NULL which would never trigger on error, potentially leading to an invalid pointer dereference. Use IS_ERR() and PTR_ERR() to properly handle the error case. Fixes: e88500247dc3 ("gpio: add QIXIS FPGA GPIO controller") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260320-qixis-v1-1-a8efc22e8945@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-qixis-fpga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c index 6e67f43ac0bdd4..3ced47db1521c8 100644 --- a/drivers/gpio/gpio-qixis-fpga.c +++ b/drivers/gpio/gpio-qixis-fpga.c @@ -60,8 +60,8 @@ static int qixis_cpld_gpio_probe(struct platform_device *pdev) return PTR_ERR(reg); regmap = devm_regmap_init_mmio(&pdev->dev, reg, ®map_config_8r_8v); - if (!regmap) - return -ENODEV; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); /* In this case, the offset of our register is 0 inside the * regmap area that we just created. From 907150bbe566e23714a25d7bcb910f236c3c44c0 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Feb 2026 11:07:35 +0200 Subject: [PATCH 102/700] ARM: dts: microchip: sam9x7: fix gpio-lines count for pioB The pioB controller on the SAM9X7 SoC actually supports 27 GPIO lines. The previous value of 26 was incorrect, leading to the last pin being unavailable for use by the GPIO subsystem. Update the #gpio-lines property to reflect the correct hardware specification. Fixes: 41af45af8bc3 ("ARM: dts: at91: sam9x7: add device tree for SoC") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260209090735.2016-1-mihai.sain@microchip.com Signed-off-by: Claudiu Beznea --- arch/arm/boot/dts/microchip/sam9x7.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/microchip/sam9x7.dtsi b/arch/arm/boot/dts/microchip/sam9x7.dtsi index 46dacbbd201ddb..d242d7a934d0fa 100644 --- a/arch/arm/boot/dts/microchip/sam9x7.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x7.dtsi @@ -1226,7 +1226,7 @@ interrupt-controller; #gpio-cells = <2>; gpio-controller; - #gpio-lines = <26>; + #gpio-lines = <27>; clocks = <&pmc PMC_TYPE_PERIPHERAL 3>; }; From fbffb8c7c7bb4d38e9f65e0bee446685011de5d8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 21 Mar 2026 15:42:40 +0800 Subject: [PATCH 103/700] reset: gpio: fix double free in reset_add_gpio_aux_device() error path When __auxiliary_device_add() fails, reset_add_gpio_aux_device() calls auxiliary_device_uninit(adev). The device release callback reset_gpio_aux_device_release() frees adev, but the current error path then calls kfree(adev) again, causing a double free. Keep kfree(adev) for the auxiliary_device_init() failure path, but avoid freeing adev after auxiliary_device_uninit(). Fixes: 5fc4e4cf7a22 ("reset: gpio: use software nodes to setup the GPIO lookup") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index fceec45c8afc1e..352c2360603bcf 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -856,7 +856,6 @@ static int reset_add_gpio_aux_device(struct device *parent, ret = __auxiliary_device_add(adev, "reset"); if (ret) { auxiliary_device_uninit(adev); - kfree(adev); return ret; } From a0e0c2f8c5f32b675f58e25a9338283cedb5ad2b Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 20 Mar 2026 11:06:17 +0000 Subject: [PATCH 104/700] reset: spacemit: k3: Decouple composite reset lines Instead of grouping several different reset lines into one composite reset, decouple them to individual ones which make it more aligned with underlying hardware. And for DWC USB driver, it will match well with the number of the reset property in the DT bindings. The DWC3 USB host controller in K3 SoC has three reset lines - AHB, VCC, PHY. The PCIe controller also has three reset lines - DBI, Slave, Master. Also three reset lines each for UCIE and RCPU block. As an agreement with maintainer, the reset IDs has been rearranged as contiguous number but keep most part unchanged to avoid break patches which already sent to mailing list. The changes of DT binding header file and reset driver are merged together as one single commit to avoid git-bisect breakage. Fixes: 938ce3b16582 ("reset: spacemit: Add SpacemiT K3 reset driver") Fixes: 216e0a5e98e5 ("dt-bindings: soc: spacemit: Add K3 reset support and IDs") Signed-off-by: Yixun Lan Reviewed-by: Philipp Zabel Acked-by: Conor Dooley Signed-off-by: Philipp Zabel --- drivers/reset/spacemit/reset-spacemit-k3.c | 60 +++++++++++-------- .../dt-bindings/reset/spacemit,k3-resets.h | 48 +++++++++++---- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c index e9e32e4c1ba562..9841f5e057b2a0 100644 --- a/drivers/reset/spacemit/reset-spacemit-k3.c +++ b/drivers/reset/spacemit/reset-spacemit-k3.c @@ -112,16 +112,21 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)), - [RESET_APMU_USB2] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(1)|BIT(2)|BIT(3)), - [RESET_APMU_USB3_PORTA] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(5)|BIT(6)|BIT(7)), - [RESET_APMU_USB3_PORTB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(9)|BIT(10)|BIT(11)), - [RESET_APMU_USB3_PORTC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(13)|BIT(14)|BIT(15)), - [RESET_APMU_USB3_PORTD] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(17)|BIT(18)|BIT(19)), + [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)), + [RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)), + [RESET_APMU_USB3_A_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(6)), + [RESET_APMU_USB3_A_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(7)), + [RESET_APMU_USB3_B_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(9)), + [RESET_APMU_USB3_B_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(10)), + [RESET_APMU_USB3_B_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(11)), + [RESET_APMU_USB3_C_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(13)), + [RESET_APMU_USB3_C_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(14)), + [RESET_APMU_USB3_C_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(15)), + [RESET_APMU_USB3_D_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(17)), + [RESET_APMU_USB3_D_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(18)), + [RESET_APMU_USB3_D_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(19)), [RESET_APMU_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)), @@ -151,10 +156,12 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_CPU7_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(26), 0), [RESET_APMU_C1_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(28), 0), [RESET_APMU_MPSUB_DBG] = RESET_DATA(APMU_PMU_CC2_AP, BIT(29), 0), - [RESET_APMU_UCIE] = RESET_DATA(APMU_UCIE_CTRL, - BIT(1) | BIT(2) | BIT(3), 0), - [RESET_APMU_RCPU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, - BIT(3) | BIT(2) | BIT(0)), + [RESET_APMU_UCIE_IP] = RESET_DATA(APMU_UCIE_CTRL, BIT(1), 0), + [RESET_APMU_UCIE_HOT] = RESET_DATA(APMU_UCIE_CTRL, BIT(2), 0), + [RESET_APMU_UCIE_MON] = RESET_DATA(APMU_UCIE_CTRL, BIT(3), 0), + [RESET_APMU_RCPU_AUDIO_SYS] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_RCPU_MCU_CORE] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_RCPU_AUDIO_APMU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(3)), [RESET_APMU_DSI4LN2_ESCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(3)), [RESET_APMU_DSI4LN2_LCD_SW] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(4)), [RESET_APMU_DSI4LN2_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(9)), @@ -164,16 +171,21 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_UFS_ACLK] = RESET_DATA(APMU_UFS_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_EDP0] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(0)), [RESET_APMU_EDP1] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(16)), - [RESET_APMU_PCIE_PORTA] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTB] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTC] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTD] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, - BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_A_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(3)), + [RESET_APMU_PCIE_A_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(4)), + [RESET_APMU_PCIE_A_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(5)), + [RESET_APMU_PCIE_B_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(3)), + [RESET_APMU_PCIE_B_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(4)), + [RESET_APMU_PCIE_B_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(5)), + [RESET_APMU_PCIE_C_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(3)), + [RESET_APMU_PCIE_C_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(4)), + [RESET_APMU_PCIE_C_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(5)), + [RESET_APMU_PCIE_D_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(3)), + [RESET_APMU_PCIE_D_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(4)), + [RESET_APMU_PCIE_D_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(5)), + [RESET_APMU_PCIE_E_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(3)), + [RESET_APMU_PCIE_E_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(4)), + [RESET_APMU_PCIE_E_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(5)), [RESET_APMU_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_EMAC2] = RESET_DATA(APMU_EMAC2_CLK_RES_CTRL, 0, BIT(1)), diff --git a/include/dt-bindings/reset/spacemit,k3-resets.h b/include/dt-bindings/reset/spacemit,k3-resets.h index 79ac1c22b7b544..dc1ef009ba7972 100644 --- a/include/dt-bindings/reset/spacemit,k3-resets.h +++ b/include/dt-bindings/reset/spacemit,k3-resets.h @@ -97,11 +97,11 @@ #define RESET_APMU_SDH0 13 #define RESET_APMU_SDH1 14 #define RESET_APMU_SDH2 15 -#define RESET_APMU_USB2 16 -#define RESET_APMU_USB3_PORTA 17 -#define RESET_APMU_USB3_PORTB 18 -#define RESET_APMU_USB3_PORTC 19 -#define RESET_APMU_USB3_PORTD 20 +#define RESET_APMU_USB2_AHB 16 +#define RESET_APMU_USB2_VCC 17 +#define RESET_APMU_USB2_PHY 18 +#define RESET_APMU_USB3_A_AHB 19 +#define RESET_APMU_USB3_A_VCC 20 #define RESET_APMU_QSPI 21 #define RESET_APMU_QSPI_BUS 22 #define RESET_APMU_DMA 23 @@ -132,8 +132,8 @@ #define RESET_APMU_CPU7_SW 48 #define RESET_APMU_C1_MPSUB_SW 49 #define RESET_APMU_MPSUB_DBG 50 -#define RESET_APMU_UCIE 51 -#define RESET_APMU_RCPU 52 +#define RESET_APMU_USB3_A_PHY 51 /* USB3 A */ +#define RESET_APMU_USB3_B_AHB 52 #define RESET_APMU_DSI4LN2_ESCCLK 53 #define RESET_APMU_DSI4LN2_LCD_SW 54 #define RESET_APMU_DSI4LN2_LCD_MCLK 55 @@ -143,16 +143,40 @@ #define RESET_APMU_UFS_ACLK 59 #define RESET_APMU_EDP0 60 #define RESET_APMU_EDP1 61 -#define RESET_APMU_PCIE_PORTA 62 -#define RESET_APMU_PCIE_PORTB 63 -#define RESET_APMU_PCIE_PORTC 64 -#define RESET_APMU_PCIE_PORTD 65 -#define RESET_APMU_PCIE_PORTE 66 +#define RESET_APMU_USB3_B_VCC 62 /* USB3 B */ +#define RESET_APMU_USB3_B_PHY 63 +#define RESET_APMU_USB3_C_AHB 64 +#define RESET_APMU_USB3_C_VCC 65 +#define RESET_APMU_USB3_C_PHY 66 #define RESET_APMU_EMAC0 67 #define RESET_APMU_EMAC1 68 #define RESET_APMU_EMAC2 69 #define RESET_APMU_ESPI_MCLK 70 #define RESET_APMU_ESPI_SCLK 71 +#define RESET_APMU_USB3_D_AHB 72 /* USB3 D */ +#define RESET_APMU_USB3_D_VCC 73 +#define RESET_APMU_USB3_D_PHY 74 +#define RESET_APMU_UCIE_IP 75 +#define RESET_APMU_UCIE_HOT 76 +#define RESET_APMU_UCIE_MON 77 +#define RESET_APMU_RCPU_AUDIO_SYS 78 +#define RESET_APMU_RCPU_MCU_CORE 79 +#define RESET_APMU_RCPU_AUDIO_APMU 80 +#define RESET_APMU_PCIE_A_DBI 81 +#define RESET_APMU_PCIE_A_SLAVE 82 +#define RESET_APMU_PCIE_A_MASTER 83 +#define RESET_APMU_PCIE_B_DBI 84 +#define RESET_APMU_PCIE_B_SLAVE 85 +#define RESET_APMU_PCIE_B_MASTER 86 +#define RESET_APMU_PCIE_C_DBI 87 +#define RESET_APMU_PCIE_C_SLAVE 88 +#define RESET_APMU_PCIE_C_MASTER 89 +#define RESET_APMU_PCIE_D_DBI 90 +#define RESET_APMU_PCIE_D_SLAVE 91 +#define RESET_APMU_PCIE_D_MASTER 92 +#define RESET_APMU_PCIE_E_DBI 93 +#define RESET_APMU_PCIE_E_SLAVE 94 +#define RESET_APMU_PCIE_E_MASTER 95 /* DCIU resets*/ #define RESET_DCIU_HDMA 0 From 6896ca5a9d05275fbeb38640c9bbdb95698de188 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 01:19:26 +0800 Subject: [PATCH 105/700] arm64: dts: allwinner: sun55i: Fix r-spi DMA r-spi has DRQs for both the main and MCU DMA controllers on the A523 SoC family, however it seems it that it is mainly routed to the MCU DMA controller, with no obvious way to change it. Change the DMA channels of r-spi to the MCU so that it works properly. Fixes: 1bec3bd1f839 ("arm64: dts: allwinner: sun55i: Add SPI controllers") Acked-by: Jernej Skrabec Link: https://patch.msgid.link/20260323171927.1256507-1-wens@kernel.org Signed-off-by: Chen-Yu Tsai --- arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index 9335977751e285..a4230205c02b2b 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -901,7 +901,7 @@ interrupts = ; clocks = <&r_ccu CLK_BUS_R_SPI>, <&r_ccu CLK_R_SPI>; clock-names = "ahb", "mod"; - dmas = <&dma 53>, <&dma 53>; + dmas = <&mcu_dma 13>, <&mcu_dma 13>; dma-names = "rx", "tx"; resets = <&r_ccu RST_BUS_R_SPI>; status = "disabled"; From 966a08c293cb9290d3fe932961404e87b3f81327 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 22 Jan 2026 17:40:40 +0530 Subject: [PATCH 106/700] dt-bindings: display: msm: qcm2290-mdss: Fix iommus property Fix IOMMU DT propety for display via dropping SMMU stream IDs which relates to secure context bank. Assigning Linux kernel (HLOS) VMID to secure context bank stream IDs is incorrect. The maximum value for iommus property is updated accordingly. These DT bindings changes should be backwards compatible. Signed-off-by: Sumit Garg Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260122121042.579270-2-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml index f0cdb542268851..2772cdec7e42ab 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml @@ -33,7 +33,7 @@ properties: - const: core iommus: - maxItems: 2 + maxItems: 1 interconnects: items: @@ -107,9 +107,7 @@ examples: interconnect-names = "mdp0-mem", "cpu-cfg"; - iommus = <&apps_smmu 0x420 0x2>, - <&apps_smmu 0x421 0x0>; - ranges; + iommus = <&apps_smmu 0x420 0x2>; display-controller@5e01000 { compatible = "qcom,qcm2290-dpu"; From 7e59cd4ad586afd87f67491cf91fa1141292cf57 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 22 Jan 2026 17:40:41 +0530 Subject: [PATCH 107/700] dt-bindings: media: venus: Fix iommus property Fix IOMMU DT propety for venus via dropping SMMU stream IDs which relates to secure context bank. Assigning Linux kernel (HLOS) VMID to secure context bank stream IDs is incorrect. The maximum value for iommus property is updated accordingly. These DT bindings changes should be backwards compatible. Signed-off-by: Sumit Garg Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260122121042.579270-3-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/media/qcom,qcm2290-venus.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml index 3f3ee82fc878b1..7e6dc410c2d2bd 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml @@ -42,7 +42,7 @@ properties: - const: vcodec0_bus iommus: - maxItems: 5 + maxItems: 2 interconnects: maxItems: 2 @@ -102,10 +102,7 @@ examples: memory-region = <&pil_video_mem>; iommus = <&apps_smmu 0x860 0x0>, - <&apps_smmu 0x880 0x0>, - <&apps_smmu 0x861 0x04>, - <&apps_smmu 0x863 0x0>, - <&apps_smmu 0x804 0xe0>; + <&apps_smmu 0x880 0x0>; interconnects = <&mmnrt_virt MASTER_VIDEO_P0 RPM_ALWAYS_TAG &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>, From 2c409e03fc04b5cded81b7add9ce509706c922e3 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 22 Jan 2026 17:40:42 +0530 Subject: [PATCH 108/700] arm64: dts: qcom: agatti: Fix IOMMU DT properties Fix IOMMU DT propeties for GPU, display and video peripherals via dropping SMMU stream IDs which relates to secure context bank. This problem only surfaced when the Gunyah based firmware stack is ported on Agatti replacing the legacy QHEE based firmware stack. Assigning Linux kernel (HLOS) VMID to secure context bank stream IDs is treated as a fault by Gunyah hypervisor which were previously ignored by QHEE hypervisor. The DT changes should be backwards compatible with legacy QHEE based firmware stack too. Suggested-by: Prakash Gupta Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Reviewed-by: Dmitry Baryshkov Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260122121042.579270-4-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/agatti.dtsi | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/agatti.dtsi b/arch/arm64/boot/dts/qcom/agatti.dtsi index 76b93b7bd50f9c..893cb06890130a 100644 --- a/arch/arm64/boot/dts/qcom/agatti.dtsi +++ b/arch/arm64/boot/dts/qcom/agatti.dtsi @@ -1669,8 +1669,7 @@ &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>; interconnect-names = "gfx-mem"; - iommus = <&adreno_smmu 0 1>, - <&adreno_smmu 2 0>; + iommus = <&adreno_smmu 0 1>; operating-points-v2 = <&gpu_opp_table>; power-domains = <&rpmpd QCM2290_VDDCX>; qcom,gmu = <&gmu_wrapper>; @@ -1951,8 +1950,7 @@ power-domains = <&dispcc MDSS_GDSC>; - iommus = <&apps_smmu 0x420 0x2>, - <&apps_smmu 0x421 0x0>; + iommus = <&apps_smmu 0x420 0x2>; interconnects = <&mmrt_virt MASTER_MDP0 RPM_ALWAYS_TAG &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>, <&bimc MASTER_APPSS_PROC RPM_ALWAYS_TAG @@ -2436,10 +2434,7 @@ memory-region = <&pil_video_mem>; iommus = <&apps_smmu 0x860 0x0>, - <&apps_smmu 0x880 0x0>, - <&apps_smmu 0x861 0x04>, - <&apps_smmu 0x863 0x0>, - <&apps_smmu 0x804 0xe0>; + <&apps_smmu 0x880 0x0>; interconnects = <&mmnrt_virt MASTER_VIDEO_P0 RPM_ALWAYS_TAG &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>, From ec66ec6a5a8f53e7c70085749e8d68f4431c630f Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Tue, 24 Mar 2026 11:33:24 +0200 Subject: [PATCH 109/700] wifi: iwlwifi: mld: Fix MLO scan timing Calculate MLO scan start time based on actual scan start notification from firmware instead of recording time when scan command is sent. Currently, MLO scan start time was captured immediately after sending the scan command to firmware. However, the actual scan start time may differ due to the FW being busy with a previous scan. In that case, the link selection code will think that the MLO scan is too old, and will warn. To fix it, Implement start scan notification handling to capture the precise moment when firmware begins the scan operation. Fixes: 9324731b9985 ("wifi: iwlwifi: mld: avoid selecting bad links") Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260324113316.4c56b8bac533.I6e656d8cc30bb82c96aabadedd62bd67f4c46bf9@changeid --- .../wireless/intel/iwlwifi/fw/api/commands.h | 5 ++++ .../net/wireless/intel/iwlwifi/fw/api/scan.h | 10 +++++++ drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 + drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 4 +-- .../net/wireless/intel/iwlwifi/mld/notif.c | 5 ++++ drivers/net/wireless/intel/iwlwifi/mld/scan.c | 30 +++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mld/scan.h | 9 ++++-- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 8d64a271bb9452..36159a76991674 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -296,6 +296,11 @@ enum iwl_legacy_cmds { */ SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + /** + * @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start + */ + SCAN_START_NOTIFICATION_UMAC = 0xb2, + /** * @MATCH_FOUND_NOTIFICATION: scan match found */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 60f0a4924ddfb0..46fcc32608e34e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -1156,6 +1156,16 @@ enum iwl_umac_scan_abort_status { IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND, }; +/** + * struct iwl_umac_scan_start - scan start notification + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @reserved: for future use + */ +struct iwl_umac_scan_start { + __le32 uid; + __le32 reserved; +} __packed; /* SCAN_START_UMAC_API_S_VER_1 */ + /** * struct iwl_umac_scan_complete - scan complete notification * @uid: scan id, &enum iwl_umac_scan_uid_offsets diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 495e9d8f3af621..9af79297c3b6ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { HCMD_NAME(MISSED_BEACONS_NOTIFICATION), HCMD_NAME(MAC_PM_POWER_TABLE), HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(SCAN_START_NOTIFICATION_UMAC), HCMD_NAME(RSS_CONFIG_CMD), HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), HCMD_NAME(REPLY_RX_MPDU_CMD), diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index f842f5183223b2..fbff5915f7fd56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -739,7 +739,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld, /* Ignore any BSS that was not seen in the last MLO scan */ if (ktime_before(link_conf->bss->ts_boottime, - mld->scan.last_mlo_scan_time)) + mld->scan.last_mlo_scan_start_time)) continue; data[n_data].link_id = link_id; @@ -945,7 +945,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld, if (!mld_vif->authorized || hweight16(usable_links) <= 1) return; - if (WARN(ktime_before(mld->scan.last_mlo_scan_time, + if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time, ktime_sub_ns(ktime_get_boottime_ns(), 5ULL * NSEC_PER_SEC)), "Last MLO scan was too long ago, can't select links\n")) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index 240526d8b63268..9c88a8579a7599 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -287,6 +287,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld, * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY. */ +CMD_VERSIONS(scan_start_notif, + CMD_VER_ENTRY(1, iwl_umac_scan_start)) CMD_VERSIONS(scan_complete_notif, CMD_VER_ENTRY(1, iwl_umac_scan_complete)) CMD_VERSIONS(scan_iter_complete_notif, @@ -360,6 +362,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif, link_id) DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity) DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid) +DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid) DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, mac_id) DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, @@ -402,6 +405,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { RX_HANDLER_SYNC) RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif, RX_HANDLER_SYNC) + RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC, + scan_start_notif) RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC, scan_complete_notif) RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index a1a4cf3ab3d311..abd4281b4b0ed6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -473,6 +473,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN; + if (scan_status == IWL_MLD_SCAN_INT_MLO) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START; + if (params->enable_6ghz_passive) flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN; @@ -1817,9 +1820,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, IWL_MLD_SCAN_INT_MLO); - if (!ret) - mld->scan.last_mlo_scan_time = ktime_get_boottime_ns(); - IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); } @@ -1904,6 +1904,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, ieee80211_sched_scan_results(mld->hw); } +void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_complete *notif = (void *)pkt->data; + u32 uid = le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status), + "FW reports scan UID %d we didn't trigger\n", uid)) + return; + + IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid, + mld->scan.uid_status[uid]); + if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO, + "FW reports scan start notification %d we didn't trigger\n", + mld->scan.uid_status[uid])) + return; + + mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns(); +} + void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h index 69110f0cfc8e2a..de5620e7f463b5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld, void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); @@ -114,8 +117,8 @@ enum iwl_mld_traffic_load { * in jiffies. * @last_start_time_jiffies: stores the last start time in jiffies * (interface up/reset/resume). - * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since - * boot. + * @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds + * since boot. */ struct iwl_mld_scan { /* Add here fields that need clean up on restart */ @@ -136,7 +139,7 @@ struct iwl_mld_scan { void *cmd; unsigned long last_6ghz_passive_jiffies; unsigned long last_start_time_jiffies; - u64 last_mlo_scan_time; + u64 last_mlo_scan_start_time; }; /** From 323156c3541e23da7e582008a7ac30cd51b60acd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Mar 2026 11:33:25 +0200 Subject: [PATCH 110/700] wifi: iwlwifi: mvm: don't send a 6E related command when not supported MCC_ALLOWED_AP_TYPE_CMD is related to 6E support. Do not send it if the device doesn't support 6E. Apparently, the firmware is mistakenly advertising support for this command even on AX201 which does not support 6E and then the firmware crashes. Fixes: 0d2fc8821a7d ("wifi: iwlwifi: nvm: parse the VLP/AFC bit from regulatory") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220804 Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260324113316.e171f0163f2a.I0c444d1f82d1773054e7ffc391ad49697d58f44e@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 43cf94c9a36bdf..6cc78661116e5d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -470,7 +470,8 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; - if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 || + !mvm->trans->cfg->uhb_supported) { IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n"); return; } From 687a95d204e72e52f2e6bc7a994cc82f76b2678f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Mar 2026 11:33:26 +0200 Subject: [PATCH 111/700] wifi: iwlwifi: mld: correctly set wifi generation data In each MAC context, the firmware expects the wifi generation data, i.e. whether or not HE/EHT (and in the future UHR) is enabled on that MAC. However, this is currently handled wrong in two ways: - EHT is only enabled when the interface is also an MLD, but we currently allow (despite the spec) connecting with EHT but without MLO. - when HE or EHT are used by TDLS peers, the firmware needs to have them enabled regardless of the AP Fix this by iterating setting up the data depending on the interface type: - for AP, just set it according to the BSS configuration - for monitor, set it according to HW capabilities - otherwise, particularly for client, iterate all stations and then their links on the interface in question and set according to their capabilities, this handles the AP and TDLS peers. Re-calculate this whenever a TDLS station is marked associated or removed so that it's kept updated, for the AP it's already updated on assoc/disassoc. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260319110722.404713b22177.Ic972b5e557d011a5438f8f97c1e793cc829e2ea9@changeid Link: https://patch.msgid.link/20260324093333.2953495-1-miriam.rachel.korenblit@intel.com --- .../net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++++++++++------ .../net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++++ 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 29df747c893801..9215fc7e2eca70 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, IEEE80211_HE_MAC_CAP2_ACK_EN); } -static void iwl_mld_set_he_support(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct iwl_mac_config_cmd *cmd) +struct iwl_mld_mac_wifi_gen_sta_iter_data { + struct ieee80211_vif *vif; + struct iwl_mac_wifi_gen_support *support; +}; + +static void iwl_mld_mac_wifi_gen_sta_iter(void *_data, + struct ieee80211_sta *sta) { - if (vif->type == NL80211_IFTYPE_AP) - cmd->wifi_gen.he_ap_support = 1; - else - cmd->wifi_gen.he_support = 1; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + if (mld_sta->vif != data->vif) + return; + + for_each_sta_active_link(data->vif, sta, link_sta, link_id) { + if (link_sta->he_cap.has_he) + data->support->he_support = 1; + if (link_sta->eht_cap.has_eht) + data->support->eht_support = 1; + } +} + +static void iwl_mld_set_wifi_gen(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_wifi_gen_support *support) +{ + struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = { + .vif = vif, + .support = support, + }; + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + /* for sniffer, set to HW capabilities */ + support->he_support = 1; + support->eht_support = mld->trans->cfg->eht_supported; + break; + case NL80211_IFTYPE_AP: + /* for AP set according to the link configs */ + for_each_vif_active_link(vif, link_conf, link_id) { + support->he_ap_support |= link_conf->he_support; + support->eht_support |= link_conf->eht_support; + } + break; + default: + /* + * If we have MLO enabled, then the firmware needs to enable + * address translation for the station(s) we add. That depends + * on having EHT enabled in firmware, which in turn depends on + * mac80211 in the iteration below. + * However, mac80211 doesn't enable capabilities on the AP STA + * until it has parsed the association response successfully, + * so set EHT (and HE as a pre-requisite for EHT) when the vif + * is an MLD. + */ + if (ieee80211_vif_is_mld(vif)) { + support->he_support = 1; + support->eht_support = 1; + } + + ieee80211_iterate_stations_mtx(mld->hw, + iwl_mld_mac_wifi_gen_sta_iter, + &sta_iter_data); + break; + } } /* fill the common part for all interface types */ @@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, u32 action) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - unsigned int link_id; lockdep_assert_wiphy(mld->wiphy); @@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, cmd->nic_not_ack_enabled = cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif)); - /* If we have MLO enabled, then the firmware needs to enable - * address translation for the station(s) we add. That depends - * on having EHT enabled in firmware, which in turn depends on - * mac80211 in the code below. - * However, mac80211 doesn't enable HE/EHT until it has parsed - * the association response successfully, so just skip all that - * and enable both when we have MLO. - */ - if (ieee80211_vif_is_mld(vif)) { - iwl_mld_set_he_support(mld, vif, cmd); - cmd->wifi_gen.eht_support = 1; - return; - } - - for_each_vif_active_link(vif, link_conf, link_id) { - if (!link_conf->he_support) - continue; - - iwl_mld_set_he_support(mld, vif, cmd); - - /* EHT, if supported, was already set above */ - break; - } + iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen); } static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 0c53d6bd96514c..71a9a72c9ac0ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1761,6 +1761,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, if (vif->type == NL80211_IFTYPE_STATION) iwl_mld_link_set_2mhz_block(mld, vif, sta); + + if (sta->tdls) { + /* + * update MAC since wifi generation flags may change, + * we also update MAC on association to the AP via the + * vif assoc change + */ + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + } + /* Now the link_sta's capabilities are set, update the FW */ iwl_mld_config_tlc(mld, vif, sta); @@ -1873,6 +1883,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, /* just removed last TDLS STA, so enable PM */ iwl_mld_update_mac_power(mld, vif, false); } + + if (sta->tdls) { + /* + * update MAC since wifi generation flags may change, + * we also update MAC on disassociation to the AP via + * the vif assoc change + */ + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + } } else { return -EINVAL; } From e225b36f83d7926c1f2035923bb0359d851fdb73 Mon Sep 17 00:00:00 2001 From: Reshma Immaculate Rajkumar Date: Thu, 19 Mar 2026 12:26:08 +0530 Subject: [PATCH 112/700] wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session During ongoing traffic, a request to stop an AMPDU session for one TID could incorrectly affect other active sessions. This can happen because an incorrect TID reference would be passed when updating the BA session state, causing the wrong session to be stopped. As a result, the affected session would be reduced to a minimal BA size, leading to a noticeable throughput degradation. Fix this issue by passing the correct argument from ath11k_dp_rx_ampdu_stop() to ath11k_peer_rx_tid_reo_update() during a stop AMPDU session. Instead of passing peer->tx_tid, which is the base address of the array, corresponding to TID 0; pass the value of &peer->rx_tid[params->tid], where the different TID numbers are accounted for. Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f2895 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Reshma Immaculate Rajkumar Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260319065608.2408179-1-reshma.rajkumar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 49d959b2e1480e..85defe11750d59 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -1110,9 +1110,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta); + struct dp_rx_tid *rx_tid; int vdev_id = arsta->arvif->vdev_id; - dma_addr_t paddr; - bool active; int ret; spin_lock_bh(&ab->base_lock); @@ -1124,15 +1123,14 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, return -ENOENT; } - paddr = peer->rx_tid[params->tid].paddr; - active = peer->rx_tid[params->tid].active; + rx_tid = &peer->rx_tid[params->tid]; - if (!active) { + if (!rx_tid->active) { spin_unlock_bh(&ab->base_lock); return 0; } - ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false); + ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, 1, 0, false); spin_unlock_bh(&ab->base_lock); if (ret) { ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n", @@ -1141,7 +1139,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, } ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, - params->sta->addr, paddr, + params->sta->addr, + rx_tid->paddr, params->tid, 1, 1); if (ret) ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n", From 4242625f272974dd1947f73b10d884eab3b277cd Mon Sep 17 00:00:00 2001 From: Reshma Immaculate Rajkumar Date: Fri, 27 Feb 2026 16:31:23 +0530 Subject: [PATCH 113/700] wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session With traffic ongoing for data TID [TID 0], an DELBA request to stop AMPDU for the BA session was received on management TID [TID 4]. The corresponding TID number was incorrectly passed to stop the BA session, resulting in the BA session for data TIDs being stopped and the BA size being reduced to 1, causing an overall dip in TCP throughput. Fix this issue by passing the correct argument from ath12k_dp_rx_ampdu_stop() to ath12k_dp_arch_peer_rx_tid_reo_update() during an AMPDU stop session. Instead of passing peer->dp_peer->rx_tid, which is the base address of the array, corresponding to TID 0, pass the value of &peer->dp_peer->rx_tid[params->tid]. With this, the different TID numbers are accounted for. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Reshma Immaculate Rajkumar Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260227110123.3726354-1-reshma.rajkumar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 5a82ede65dd3da..244d5230a5bd89 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -735,6 +735,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct ath12k_dp_link_peer *peer; struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta); + struct ath12k_dp_rx_tid *rx_tid; struct ath12k_link_sta *arsta; int vdev_id; bool active; @@ -770,7 +771,8 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, return 0; } - ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, peer->dp_peer->rx_tid, + rx_tid = &peer->dp_peer->rx_tid[params->tid]; + ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid, 1, 0, false); spin_unlock_bh(&dp->dp_lock); if (ret) { From d049e56b1739101d1c4d81deedb269c52a8dbba0 Mon Sep 17 00:00:00 2001 From: Yasuaki Torimaru Date: Tue, 24 Mar 2026 19:06:24 +0900 Subject: [PATCH 114/700] wifi: wilc1000: fix u8 overflow in SSID scan buffer size calculation The variable valuesize is declared as u8 but accumulates the total length of all SSIDs to scan. Each SSID contributes up to 33 bytes (IEEE80211_MAX_SSID_LEN + 1), and with WILC_MAX_NUM_PROBED_SSID (10) SSIDs the total can reach 330, which wraps around to 74 when stored in a u8. This causes kmalloc to allocate only 75 bytes while the subsequent memcpy writes up to 331 bytes into the buffer, resulting in a 256-byte heap buffer overflow. Widen valuesize from u8 to u32 to accommodate the full range. Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver") Cc: stable@vger.kernel.org Signed-off-by: Yasuaki Torimaru Link: https://patch.msgid.link/20260324100624.983458-1-yasuakitorimaru@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/hif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index f354b11cb91978..944b2a812b63e5 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source, u32 index = 0; u32 i, scan_timeout; u8 *buffer; - u8 valuesize = 0; + u32 valuesize = 0; u8 *search_ssid_vals = NULL; const u8 ch_list_len = request->n_channels; struct host_if_drv *hif_drv = vif->hif_drv; From 0fd56fad9c56356e7fa7a7c52e7ecbf807a44eb0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 23 Mar 2026 16:08:45 +0800 Subject: [PATCH 115/700] wifi: wl1251: validate packet IDs before indexing tx_frames wl1251_tx_packet_cb() uses the firmware completion ID directly to index the fixed 16-entry wl->tx_frames[] array. The ID is a raw u8 from the completion block, and the callback does not currently verify that it fits the array before dereferencing it. Reject completion IDs that fall outside wl->tx_frames[] and keep the existing NULL check in the same guard. This keeps the fix local to the trust boundary and avoids touching the rest of the completion flow. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260323080845.40033-1-pengpeng@iscas.ac.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wl1251/tx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c index 2da8c0d5105bd3..4489aa77bb0fa9 100644 --- a/drivers/net/wireless/ti/wl1251/tx.c +++ b/drivers/net/wireless/ti/wl1251/tx.c @@ -402,12 +402,14 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, int hdrlen; u8 *frame; - skb = wl->tx_frames[result->id]; - if (skb == NULL) { - wl1251_error("SKB for packet %d is NULL", result->id); + if (unlikely(result->id >= ARRAY_SIZE(wl->tx_frames) || + wl->tx_frames[result->id] == NULL)) { + wl1251_error("invalid packet id %u", result->id); return; } + skb = wl->tx_frames[result->id]; + info = IEEE80211_SKB_CB(skb); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && From 744fabc338e87b95c4d1ff7c95bc8c0f834c6d99 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Sat, 7 Feb 2026 18:03:22 +0300 Subject: [PATCH 116/700] wifi: iwlwifi: mvm: fix potential out-of-bounds read in iwl_mvm_nd_match_info_handler() The memcpy function assumes the dynamic array notif->matches is at least as large as the number of bytes to copy. Otherwise, results->matches may contain unwanted data. To guarantee safety, extend the validation in one of the checks to ensure sufficient packet length. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: stable@vger.kernel.org Fixes: 5ac54afd4d97 ("wifi: iwlwifi: mvm: Add handling for scan offload match info notification") Signed-off-by: Alexey Velichayshiy Link: https://patch.msgid.link/20260207150335.1013646-1-a.velichayshiy@ispras.ru Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index a19f9d2e934635..9a74f60c91850f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2807,7 +2807,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(vif)) return; - if (len < sizeof(struct iwl_scan_offload_match_info)) { + if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) { IWL_ERR(mvm, "Invalid scan match info notification\n"); return; } From 57ad0d4a00f5d3e80f33ba2da8d560c73d83dc22 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 6 Mar 2026 13:50:31 +0100 Subject: [PATCH 117/700] s390/cpum_sf: Cap sampling rate to prevent lsctl exception commit fcc43a7e294f ("s390/configs: Set HZ=1000") changed the interrupt frequency of the system. On machines with heavy load and many perf event overflows, this might lead to an exception. Dmesg displays these entries: [112.242542] cpum_sf: Loading sampling controls failed: op 1 err -22 One line per CPU online. The root cause is the CPU Measurement sampling facility overflow adjustment. Whenever an overflow (too much samples per tick) occurs, the sampling rate is adjusted and increased. This was done without observing the maximum sampling rate limit. When the current sampling interval is higher than the maximum sampling rate limit, the lsctl instruction raises an exception. The error messages is the result of such an exception. Observe the upper limit when the new sampling rate is recalculated. Cc: stable@vger.kernel.org Fixes: 39d4a501a9ef ("s390/cpum_sf: Adjust sampling interval to avoid hitting sample limits") Signed-off-by: Thomas Richter Reviewed-by: Sumanth Korikkar Reviewed-by: Hendrik Brueckner Signed-off-by: Vasily Gorbik --- arch/s390/kernel/perf_cpum_sf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index c92c29de725e81..7bfeb5208177d2 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1168,6 +1168,7 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt, static void hw_perf_event_update(struct perf_event *event, int flush_all) { unsigned long long event_overflow, sampl_overflow, num_sdb; + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); struct hw_perf_event *hwc = &event->hw; union hws_trailer_header prev, new; struct hws_trailer_entry *te; @@ -1247,8 +1248,11 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) * are dropped. * Slightly increase the interval to avoid hitting this limit. */ - if (event_overflow) + if (event_overflow) { SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10); + if (SAMPL_RATE(hwc) > cpuhw->qsi.max_sampl_rate) + SAMPL_RATE(hwc) = cpuhw->qsi.max_sampl_rate; + } } static inline unsigned long aux_sdb_index(struct aux_buffer *aux, From c8d46f17c2fc7d25c18e60c008928aecab26184d Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 19 Mar 2026 09:06:52 +0100 Subject: [PATCH 118/700] s390/zcrypt: Fix memory leak with CCA cards used as accelerator Tests showed that there is a memory leak if CCA cards are used as accelerator for clear key RSA requests (ME and CRT). With the last rework for the memory allocation the AP messages are allocated by ap_init_apmsg() but for some reason on two places (ME and CRT) the older allocation was still in place. So the first allocation simple was never freed. Fixes: 57db62a130ce ("s390/ap/zcrypt: Rework AP message buffer allocation") Reported-by: Yi Zhang Closes: https://lore.kernel.org/linux-s390/CAHj4cs9H67Uz0iVaRQv447p7JFPRPy3TKAT4=Y6_e=wSHCZM5w@mail.gmail.com/ Reported-by: Nadja Hariz Cc: stable@vger.kernel.org Reviewed-by: Ingo Franzki Reviewed-by: Holger Dengler Acked-by: Heiko Carstens Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index a0dcab5dc4f2f7..23a32221e41a77 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -953,6 +953,10 @@ static atomic_t zcrypt_step = ATOMIC_INIT(0); /* * The request distributor calls this function if it picked the CEXxC * device to handle a modexpo request. + * This function assumes that ap_msg has been initialized with + * ap_init_apmsg() and thus a valid buffer with the size of + * ap_msg->bufsize is available within ap_msg. Also the caller has + * to make sure ap_release_apmsg() is always called even on failure. * @zq: pointer to zcrypt_queue structure that identifies the * CEXxC device to the request distributor * @mex: pointer to the modexpo request buffer @@ -964,21 +968,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq, struct ap_response_type *resp_type = &ap_msg->response; int rc; - ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); - if (!ap_msg->msg) - return -ENOMEM; - ap_msg->bufsize = PAGE_SIZE; ap_msg->receive = zcrypt_msgtype6_receive; ap_msg->psmid = (((unsigned long)current->pid) << 32) + atomic_inc_return(&zcrypt_step); rc = icamex_msg_to_type6mex_msgx(zq, ap_msg, mex); if (rc) - goto out_free; + goto out; resp_type->type = CEXXC_RESPONSE_TYPE_ICA; init_completion(&resp_type->work); rc = ap_queue_message(zq->queue, ap_msg); if (rc) - goto out_free; + goto out; rc = wait_for_completion_interruptible(&resp_type->work); if (rc == 0) { rc = ap_msg->rc; @@ -991,15 +991,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq, ap_cancel_message(zq->queue, ap_msg); } -out_free: - free_page((unsigned long)ap_msg->msg); - ap_msg->msg = NULL; +out: return rc; } /* * The request distributor calls this function if it picked the CEXxC * device to handle a modexpo_crt request. + * This function assumes that ap_msg has been initialized with + * ap_init_apmsg() and thus a valid buffer with the size of + * ap_msg->bufsize is available within ap_msg. Also the caller has + * to make sure ap_release_apmsg() is always called even on failure. * @zq: pointer to zcrypt_queue structure that identifies the * CEXxC device to the request distributor * @crt: pointer to the modexpoc_crt request buffer @@ -1011,21 +1013,17 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq, struct ap_response_type *resp_type = &ap_msg->response; int rc; - ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL); - if (!ap_msg->msg) - return -ENOMEM; - ap_msg->bufsize = PAGE_SIZE; ap_msg->receive = zcrypt_msgtype6_receive; ap_msg->psmid = (((unsigned long)current->pid) << 32) + atomic_inc_return(&zcrypt_step); rc = icacrt_msg_to_type6crt_msgx(zq, ap_msg, crt); if (rc) - goto out_free; + goto out; resp_type->type = CEXXC_RESPONSE_TYPE_ICA; init_completion(&resp_type->work); rc = ap_queue_message(zq->queue, ap_msg); if (rc) - goto out_free; + goto out; rc = wait_for_completion_interruptible(&resp_type->work); if (rc == 0) { rc = ap_msg->rc; @@ -1038,9 +1036,7 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq, ap_cancel_message(zq->queue, ap_msg); } -out_free: - free_page((unsigned long)ap_msg->msg); - ap_msg->msg = NULL; +out: return rc; } From 1b164b876c36c3eb5561dd9b37702b04401b0166 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 24 Mar 2026 10:21:25 -1000 Subject: [PATCH 119/700] cgroup: Wait for dying tasks to leave on rmdir a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup") hid PF_EXITING tasks from cgroup.procs so that systemd doesn't see tasks that have already been reaped via waitpid(). However, the populated counter (nr_populated_csets) is only decremented when the task later passes through cgroup_task_dead() in finish_task_switch(). This means cgroup.procs can appear empty while the cgroup is still populated, causing rmdir to fail with -EBUSY. Fix this by making cgroup_rmdir() wait for dying tasks to fully leave. If the cgroup is populated but all remaining tasks have PF_EXITING set (the task iterator returns none due to the existing filter), wait for a kick from cgroup_task_dead() and retry. The wait is brief as tasks are removed from the cgroup's css_set between PF_EXITING assertion in do_exit() and cgroup_task_dead() in finish_task_switch(). v2: cgroup_is_populated() true to false transition happens under css_set_lock not cgroup_mutex, so retest under css_set_lock before sleeping to avoid missed wakeups (Sebastian). Fixes: a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202603222104.2c81684e-lkp@intel.com Reported-by: Sebastian Andrzej Siewior Signed-off-by: Tejun Heo Reviewed-by: Sebastian Andrzej Siewior Cc: Bert Karwatzki Cc: Michal Koutny Cc: cgroups@vger.kernel.org --- include/linux/cgroup-defs.h | 3 ++ kernel/cgroup/cgroup.c | 86 +++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index bb92f5c169ca2d..7f87399938fa2b 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -609,6 +609,9 @@ struct cgroup { /* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; + /* used by cgroup_rmdir() to wait for dying tasks to leave */ + wait_queue_head_t dying_populated_waitq; + /* used to schedule release agent */ struct work_struct release_agent_work; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 01fc2a93f3ef22..2163054e1aa65f 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -2126,6 +2126,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) #endif init_waitqueue_head(&cgrp->offline_waitq); + init_waitqueue_head(&cgrp->dying_populated_waitq); INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent); } @@ -6224,6 +6225,76 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) return 0; }; +/** + * cgroup_drain_dying - wait for dying tasks to leave before rmdir + * @cgrp: the cgroup being removed + * + * The PF_EXITING filter in css_task_iter_advance() hides exiting tasks from + * cgroup.procs so that userspace (e.g. systemd) doesn't see tasks that have + * already been reaped via waitpid(). However, the populated counter + * (nr_populated_csets) is only decremented when the task later passes through + * cgroup_task_dead() in finish_task_switch(). This creates a window where + * cgroup.procs appears empty but cgroup_is_populated() is still true, causing + * rmdir to fail with -EBUSY. + * + * This function bridges that gap. If the cgroup is populated but all remaining + * tasks have PF_EXITING set, we wait for cgroup_task_dead() to process them. + * Tasks are removed from the cgroup's css_set in cgroup_task_dead() called from + * finish_task_switch(). As the window between PF_EXITING and cgroup_task_dead() + * is short, the number of PF_EXITING tasks on the list is small and the wait + * is brief. + * + * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we + * retry the full check from scratch. + * + * Must be called with cgroup_mutex held. + */ +static int cgroup_drain_dying(struct cgroup *cgrp) + __releases(&cgroup_mutex) __acquires(&cgroup_mutex) +{ + struct css_task_iter it; + struct task_struct *task; + DEFINE_WAIT(wait); + + lockdep_assert_held(&cgroup_mutex); +retry: + if (!cgroup_is_populated(cgrp)) + return 0; + + /* Same iterator as cgroup.threads - if any task is visible, it's busy */ + css_task_iter_start(&cgrp->self, 0, &it); + task = css_task_iter_next(&it); + css_task_iter_end(&it); + + if (task) + return -EBUSY; + + /* + * All remaining tasks are PF_EXITING and will pass through + * cgroup_task_dead() shortly. Wait for a kick and retry. + * + * cgroup_is_populated() can't transition from false to true while + * we're holding cgroup_mutex, but the true to false transition + * happens under css_set_lock (via cgroup_task_dead()). We must + * retest and prepare_to_wait() under css_set_lock. Otherwise, the + * transition can happen between our first test and + * prepare_to_wait(), and we sleep with no one to wake us. + */ + spin_lock_irq(&css_set_lock); + if (!cgroup_is_populated(cgrp)) { + spin_unlock_irq(&css_set_lock); + return 0; + } + prepare_to_wait(&cgrp->dying_populated_waitq, &wait, + TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&css_set_lock); + mutex_unlock(&cgroup_mutex); + schedule(); + finish_wait(&cgrp->dying_populated_waitq, &wait); + mutex_lock(&cgroup_mutex); + goto retry; +} + int cgroup_rmdir(struct kernfs_node *kn) { struct cgroup *cgrp; @@ -6233,9 +6304,12 @@ int cgroup_rmdir(struct kernfs_node *kn) if (!cgrp) return 0; - ret = cgroup_destroy_locked(cgrp); - if (!ret) - TRACE_CGROUP_PATH(rmdir, cgrp); + ret = cgroup_drain_dying(cgrp); + if (!ret) { + ret = cgroup_destroy_locked(cgrp); + if (!ret) + TRACE_CGROUP_PATH(rmdir, cgrp); + } cgroup_kn_unlock(kn); return ret; @@ -6995,6 +7069,7 @@ void cgroup_task_exit(struct task_struct *tsk) static void do_cgroup_task_dead(struct task_struct *tsk) { + struct cgrp_cset_link *link; struct css_set *cset; unsigned long flags; @@ -7008,6 +7083,11 @@ static void do_cgroup_task_dead(struct task_struct *tsk) if (thread_group_leader(tsk) && atomic_read(&tsk->signal->live)) list_add_tail(&tsk->cg_list, &cset->dying_tasks); + /* kick cgroup_drain_dying() waiters, see cgroup_rmdir() */ + list_for_each_entry(link, &cset->cgrp_links, cgrp_link) + if (waitqueue_active(&link->cgrp->dying_populated_waitq)) + wake_up(&link->cgrp->dying_populated_waitq); + if (dl_task(tsk)) dec_dl_tasks_cs(tsk); From 6680c162b4850976ee52b57372eddc4450c1d074 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 24 Mar 2026 10:21:47 -1000 Subject: [PATCH 120/700] selftests/cgroup: Don't require synchronous populated update on task exit test_cgcore_populated (test_core) and test_cgkill_{simple,tree,forkbomb} (test_kill) check cgroup.events "populated 0" immediately after reaping child tasks with waitpid(). This used to work because cgroup_task_exit() in do_exit() unlinked tasks from css_sets before exit_notify() woke up waitpid(). d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out") moved the unlink to cgroup_task_dead() in finish_task_switch(), which runs after exit_notify(). The populated counter is now decremented after the parent's waitpid() can return, so there is no longer a synchronous ordering guarantee. On PREEMPT_RT, where cgroup_task_dead() is further deferred through lazy irq_work, the race window is even larger. The synchronous populated transition was never part of the cgroup interface contract - it was an implementation artifact. Use cg_read_strcmp_wait() which retries for up to 1 second, matching what these tests actually need to verify: that the cgroup eventually becomes unpopulated after all tasks exit. Fixes: d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out") Reported-by: Sebastian Andrzej Siewior Signed-off-by: Tejun Heo Tested-by: Sebastian Andrzej Siewior Cc: Christian Brauner Cc: cgroups@vger.kernel.org --- tools/testing/selftests/cgroup/lib/cgroup_util.c | 15 +++++++++++++++ .../selftests/cgroup/lib/include/cgroup_util.h | 2 ++ tools/testing/selftests/cgroup/test_core.c | 3 ++- tools/testing/selftests/cgroup/test_kill.c | 7 ++++--- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index ce6c2642fd9bbb..6a7295347e90b1 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -123,6 +123,21 @@ int cg_read_strcmp(const char *cgroup, const char *control, return ret; } +int cg_read_strcmp_wait(const char *cgroup, const char *control, + const char *expected) +{ + int i, ret; + + for (i = 0; i < 100; i++) { + ret = cg_read_strcmp(cgroup, control, expected); + if (!ret) + return ret; + usleep(10000); + } + + return ret; +} + int cg_read_strstr(const char *cgroup, const char *control, const char *needle) { char buf[PAGE_SIZE]; diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h index 77f386dab5e8fb..567b1082974c53 100644 --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h @@ -61,6 +61,8 @@ extern int cg_read(const char *cgroup, const char *control, char *buf, size_t len); extern int cg_read_strcmp(const char *cgroup, const char *control, const char *expected); +extern int cg_read_strcmp_wait(const char *cgroup, const char *control, + const char *expected); extern int cg_read_strstr(const char *cgroup, const char *control, const char *needle); extern long cg_read_long(const char *cgroup, const char *control); diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index 102262555a599b..7b83c7e7c9d4fa 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -233,7 +233,8 @@ static int test_cgcore_populated(const char *root) if (err) goto cleanup; - if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) + if (cg_read_strcmp_wait(cg_test_d, "cgroup.events", + "populated 0\n")) goto cleanup; /* Remove cgroup. */ diff --git a/tools/testing/selftests/cgroup/test_kill.c b/tools/testing/selftests/cgroup/test_kill.c index c8c9d306925b63..f6cd23a8ecc71c 100644 --- a/tools/testing/selftests/cgroup/test_kill.c +++ b/tools/testing/selftests/cgroup/test_kill.c @@ -86,7 +86,7 @@ static int test_cgkill_simple(const char *root) wait_for_pid(pids[i]); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n")) ret = KSFT_FAIL; if (cgroup) @@ -190,7 +190,8 @@ static int test_cgkill_tree(const char *root) wait_for_pid(pids[i]); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup[0], "cgroup.events", + "populated 0\n")) ret = KSFT_FAIL; for (i = 9; i >= 0 && cgroup[i]; i--) { @@ -251,7 +252,7 @@ static int test_cgkill_forkbomb(const char *root) wait_for_pid(pid); if (ret == KSFT_PASS && - cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) + cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n")) ret = KSFT_FAIL; if (cgroup) From e398978ddf18fe5a2fc8299c77e6fe50e6c306c4 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 25 Mar 2026 13:34:18 +0100 Subject: [PATCH 121/700] workqueue: Better describe stall check Try to be more explicit why the workqueue watchdog does not take pool->lock by default. Spin locks are full memory barriers which delay anything. Obviously, they would primary delay operations on the related worker pools. Explain why it is enough to prevent the false positive by re-checking the timestamp under the pool->lock. Finally, make it clear what would be the alternative solution in __queue_work() which is a hotter path. Signed-off-by: Petr Mladek Acked-by: Song Liu Signed-off-by: Tejun Heo --- kernel/workqueue.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ff97b705f25ed1..eda756556341ab 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -7702,13 +7702,14 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) /* * Did we stall? * - * Do a lockless check first. On weakly ordered - * architectures, the lockless check can observe a - * reordering between worklist insert_work() and - * last_progress_ts update from __queue_work(). Since - * __queue_work() is a much hotter path than the timer - * function, we handle false positive here by reading - * last_progress_ts again with pool->lock held. + * Do a lockless check first to do not disturb the system. + * + * Prevent false positives by double checking the timestamp + * under pool->lock. The lock makes sure that the check reads + * an updated pool->last_progress_ts when this CPU saw + * an already updated pool->worklist above. It seems better + * than adding another barrier into __queue_work() which + * is a hotter path. */ if (time_after(now, ts + thresh)) { scoped_guard(raw_spinlock_irqsave, &pool->lock) { From ed8444006df9863ffa682e315352c44a49d9f4cb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 24 Mar 2026 15:33:28 +0100 Subject: [PATCH 122/700] arm64: dts: renesas: sparrow-hawk: Reserve first 128 MiB of DRAM Mark the first 128 MiB of DRAM as reserved. The first 128 MiB of DRAM may optionally be used by TFA and other firmware for its own purposes, and in such case, Linux must not use this memory. On this platform, U-Boot runs in EL3 and starts TFA BL31 and Linux from a single combined fitImage. U-Boot has full access to all memory in the 0x40000000..0xbfffffff range, as well memory in the memory banks in the 64-bit address ranges, and therefore U-Boot patches this full complete view of platform memory layout into the DT that is passed to the next stage. The next stage is TFA BL31 and then the Linux kernel. The TFA BL31 does not modify the DT passed from U-Boot to TFA BL31 and then to Linux with any new reserved-memory {} node to reserve memory areas used by the TFA BL31 to prevent the next stage from using those areas, which lets Linux to use all of the available DRAM as described in the DT that was passed in by U-Boot, including the areas that are newly utilized by TFA BL31. In case of high DRAM utilization, for example in case of four instances of "memtester 3900M" running in parallel, unless the memory used by TFA BL31 is properly reserved, Linux may use and corrupt the memory used by TFA BL31, which would often lead to system becoming unresponsive. Until TFA BL31 can properly fill its own reserved-memory node into the DT, and to assure older versions of TFA BL31 do not cause problems, add explicitly reserved-memory {} node which prevents Linux from using the first 128 MiB of DRAM. Note that TFA BL31 can be adjusted to use different memory areas, this newly added reserved-memory {} node follows longer-term practice on the R-Car SoCs where the first 128 MiB of DRAM is reserved for firmware use. In case user does modify TFA BL31 to use different memory ranges, they must either use a future version of TFA BL31 which properly patches a reserved-memory {} node into the DT, or they must adjust the address ranges of this reserved-memory {} node accordingly. Fixes: a719915e76f2 ("arm64: dts: renesas: r8a779g3: Add Retronix R-Car V4H Sparrow Hawk board support") Cc: stable@vger.kernel.org Signed-off-by: Marek Vasut Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260324143342.17872-1-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven --- arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts index ff07d984cbf299..812b133cf29ed7 100644 --- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts +++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts @@ -118,6 +118,17 @@ reg = <0x6 0x00000000 0x1 0x00000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + tfa@40000000 { + reg = <0x0 0x40000000 0x0 0x8000000>; + no-map; + }; + }; + /* Page 27 / DSI to Display */ dp-con { compatible = "dp-connector"; From 7b3b1e5a87b2f5e35c52b5386d7c327be869454f Mon Sep 17 00:00:00 2001 From: Long Li Date: Mon, 16 Mar 2026 14:07:42 -0700 Subject: [PATCH 123/700] PCI: hv: Set default NUMA node to 0 for devices without affinity info When hv_pci_assign_numa_node() processes a device that does not have HV_PCI_DEVICE_FLAG_NUMA_AFFINITY set or has an out-of-range virtual_numa_node, the device NUMA node is left unset. On x86_64, the uninitialized default happens to be 0, but on ARM64 it is NUMA_NO_NODE (-1). Tests show that when no NUMA information is available from the Hyper-V host, devices perform best when assigned to node 0. With NUMA_NO_NODE the kernel may spread work across NUMA nodes, which degrades performance on Hyper-V, particularly for high-throughput devices like MANA. Always set the device NUMA node to 0 before the conditional NUMA affinity check, so that devices get a performant default when the host provides no NUMA information, and behavior is consistent on both x86_64 and ARM64. Fixes: 999dd956d838 ("PCI: hv: Add support for protocol 1.3 and support PCI_BUS_RELATIONS2") Signed-off-by: Long Li Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/pci/controller/pci-hyperv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 2c7a406b4ba850..38a790f642a1ea 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -2485,6 +2485,14 @@ static void hv_pci_assign_numa_node(struct hv_pcibus_device *hbus) if (!hv_dev) continue; + /* + * If the Hyper-V host doesn't provide a NUMA node for the + * device, default to node 0. With NUMA_NO_NODE the kernel + * may spread work across NUMA nodes, which degrades + * performance on Hyper-V. + */ + set_dev_node(&dev->dev, 0); + if (hv_dev->desc.flags & HV_PCI_DEVICE_FLAG_NUMA_AFFINITY && hv_dev->desc.virtual_numa_node < num_possible_nodes()) /* From 789b06f9f39cdc7e895bdab2c034e39c41c8f8d6 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 25 Mar 2026 01:46:02 +0300 Subject: [PATCH 124/700] wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free Currently we execute `SET_NETDEV_DEV(dev, &priv->lowerdev->dev)` for the virt_wifi net devices. However, unregistering a virt_wifi device in netdev_run_todo() can happen together with the device referenced by SET_NETDEV_DEV(). It can result in use-after-free during the ethtool operations performed on a virt_wifi device that is currently being unregistered. Such a net device can have the `dev.parent` field pointing to the freed memory, but ethnl_ops_begin() calls `pm_runtime_get_sync(dev->dev.parent)`. Let's remove SET_NETDEV_DEV for virt_wifi to avoid bugs like this: ================================================================== BUG: KASAN: slab-use-after-free in __pm_runtime_resume+0xe2/0xf0 Read of size 2 at addr ffff88810cfc46f8 by task pm/606 Call Trace: dump_stack_lvl+0x4d/0x70 print_report+0x170/0x4f3 ? __pfx__raw_spin_lock_irqsave+0x10/0x10 kasan_report+0xda/0x110 ? __pm_runtime_resume+0xe2/0xf0 ? __pm_runtime_resume+0xe2/0xf0 __pm_runtime_resume+0xe2/0xf0 ethnl_ops_begin+0x49/0x270 ethnl_set_features+0x23c/0xab0 ? __pfx_ethnl_set_features+0x10/0x10 ? kvm_sched_clock_read+0x11/0x20 ? local_clock_noinstr+0xf/0xf0 ? local_clock+0x10/0x30 ? kasan_save_track+0x25/0x60 ? __kasan_kmalloc+0x7f/0x90 ? genl_family_rcv_msg_attrs_parse.isra.0+0x150/0x2c0 genl_family_rcv_msg_doit+0x1e7/0x2c0 ? __pfx_genl_family_rcv_msg_doit+0x10/0x10 ? __pfx_cred_has_capability.isra.0+0x10/0x10 ? stack_trace_save+0x8e/0xc0 genl_rcv_msg+0x411/0x660 ? __pfx_genl_rcv_msg+0x10/0x10 ? __pfx_ethnl_set_features+0x10/0x10 netlink_rcv_skb+0x121/0x380 ? __pfx_genl_rcv_msg+0x10/0x10 ? __pfx_netlink_rcv_skb+0x10/0x10 ? __pfx_down_read+0x10/0x10 genl_rcv+0x23/0x30 netlink_unicast+0x60f/0x830 ? __pfx_netlink_unicast+0x10/0x10 ? __pfx___alloc_skb+0x10/0x10 netlink_sendmsg+0x6ea/0xbc0 ? __pfx_netlink_sendmsg+0x10/0x10 ? __futex_queue+0x10b/0x1f0 ____sys_sendmsg+0x7a2/0x950 ? copy_msghdr_from_user+0x26b/0x430 ? __pfx_____sys_sendmsg+0x10/0x10 ? __pfx_copy_msghdr_from_user+0x10/0x10 ___sys_sendmsg+0xf8/0x180 ? __pfx____sys_sendmsg+0x10/0x10 ? __pfx_futex_wait+0x10/0x10 ? fdget+0x2e4/0x4a0 __sys_sendmsg+0x11f/0x1c0 ? __pfx___sys_sendmsg+0x10/0x10 do_syscall_64+0xe2/0x570 ? exc_page_fault+0x66/0xb0 entry_SYSCALL_64_after_hwframe+0x77/0x7f This fix may be combined with another one in the ethtool subsystem: https://lore.kernel.org/all/20260322075917.254874-1-alex.popov@linux.com/T/#u Fixes: d43c65b05b848e0b ("ethtool: runtime-resume netdev parent in ethnl_ops_begin") Cc: stable@vger.kernel.org Signed-off-by: Alexander Popov Acked-by: Greg Kroah-Hartman Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260324224607.374327-1-alex.popov@linux.com Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/virt_wifi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c index 885dc7243e8dc0..97bd39d89e98ef 100644 --- a/drivers/net/wireless/virtual/virt_wifi.c +++ b/drivers/net/wireless/virtual/virt_wifi.c @@ -557,7 +557,6 @@ static int virt_wifi_newlink(struct net_device *dev, eth_hw_addr_inherit(dev, priv->lowerdev); netif_stacked_transfer_operstate(priv->lowerdev, dev); - SET_NETDEV_DEV(dev, &priv->lowerdev->dev); dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr); if (!dev->ieee80211_ptr) { From 4c56a8ac6869855866de0bb368a4189739e1d24f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 25 Mar 2026 07:23:48 -1000 Subject: [PATCH 125/700] cgroup: Fix cgroup_drain_dying() testing the wrong condition cgroup_drain_dying() was using cgroup_is_populated() to test whether there are dying tasks to wait for. cgroup_is_populated() tests nr_populated_csets, nr_populated_domain_children and nr_populated_threaded_children, but cgroup_drain_dying() only needs to care about this cgroup's own tasks - whether there are children is cgroup_destroy_locked()'s concern. This caused hangs during shutdown. When systemd tried to rmdir a cgroup that had no direct tasks but had a populated child, cgroup_drain_dying() would enter its wait loop because cgroup_is_populated() was true from nr_populated_domain_children. The task iterator found nothing to wait for, yet the populated state never cleared because it was driven by live tasks in the child cgroup. Fix it by using cgroup_has_tasks() which only tests nr_populated_csets. v3: Fix cgroup_is_populated() -> cgroup_has_tasks() (Sebastian). v2: https://lore.kernel.org/r/20260323200205.1063629-1-tj@kernel.org Reported-by: Sebastian Andrzej Siewior Fixes: 1b164b876c36 ("cgroup: Wait for dying tasks to leave on rmdir") Signed-off-by: Tejun Heo Tested-by: Sebastian Andrzej Siewior --- kernel/cgroup/cgroup.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 2163054e1aa65f..4ca3cb993da299 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -6229,20 +6229,22 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * cgroup_drain_dying - wait for dying tasks to leave before rmdir * @cgrp: the cgroup being removed * - * The PF_EXITING filter in css_task_iter_advance() hides exiting tasks from - * cgroup.procs so that userspace (e.g. systemd) doesn't see tasks that have - * already been reaped via waitpid(). However, the populated counter - * (nr_populated_csets) is only decremented when the task later passes through + * cgroup.procs and cgroup.threads use css_task_iter which filters out + * PF_EXITING tasks so that userspace doesn't see tasks that have already been + * reaped via waitpid(). However, cgroup_has_tasks() - which tests whether the + * cgroup has non-empty css_sets - is only updated when dying tasks pass through * cgroup_task_dead() in finish_task_switch(). This creates a window where - * cgroup.procs appears empty but cgroup_is_populated() is still true, causing - * rmdir to fail with -EBUSY. + * cgroup.procs reads empty but cgroup_has_tasks() is still true, making rmdir + * fail with -EBUSY from cgroup_destroy_locked() even though userspace sees no + * tasks. + * + * This function aligns cgroup_has_tasks() with what userspace can observe. If + * cgroup_has_tasks() but the task iterator sees nothing (all remaining tasks are + * PF_EXITING), we wait for cgroup_task_dead() to finish processing them. As the + * window between PF_EXITING and cgroup_task_dead() is short, the wait is brief. * - * This function bridges that gap. If the cgroup is populated but all remaining - * tasks have PF_EXITING set, we wait for cgroup_task_dead() to process them. - * Tasks are removed from the cgroup's css_set in cgroup_task_dead() called from - * finish_task_switch(). As the window between PF_EXITING and cgroup_task_dead() - * is short, the number of PF_EXITING tasks on the list is small and the wait - * is brief. + * This function only concerns itself with this cgroup's own dying tasks. + * Whether the cgroup has children is cgroup_destroy_locked()'s problem. * * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we * retry the full check from scratch. @@ -6258,7 +6260,7 @@ static int cgroup_drain_dying(struct cgroup *cgrp) lockdep_assert_held(&cgroup_mutex); retry: - if (!cgroup_is_populated(cgrp)) + if (!cgroup_has_tasks(cgrp)) return 0; /* Same iterator as cgroup.threads - if any task is visible, it's busy */ @@ -6273,15 +6275,15 @@ static int cgroup_drain_dying(struct cgroup *cgrp) * All remaining tasks are PF_EXITING and will pass through * cgroup_task_dead() shortly. Wait for a kick and retry. * - * cgroup_is_populated() can't transition from false to true while - * we're holding cgroup_mutex, but the true to false transition - * happens under css_set_lock (via cgroup_task_dead()). We must - * retest and prepare_to_wait() under css_set_lock. Otherwise, the - * transition can happen between our first test and - * prepare_to_wait(), and we sleep with no one to wake us. + * cgroup_has_tasks() can't transition from false to true while we're + * holding cgroup_mutex, but the true to false transition happens + * under css_set_lock (via cgroup_task_dead()). We must retest and + * prepare_to_wait() under css_set_lock. Otherwise, the transition + * can happen between our first test and prepare_to_wait(), and we + * sleep with no one to wake us. */ spin_lock_irq(&css_set_lock); - if (!cgroup_is_populated(cgrp)) { + if (!cgroup_has_tasks(cgrp)) { spin_unlock_irq(&css_set_lock); return 0; } From d0b224cf9ab12e86a4d1ca55c760dfaa5c19cbe7 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Wed, 25 Mar 2026 12:32:16 +0100 Subject: [PATCH 126/700] iio: light: veml6070: fix veml6070_read() return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit veml6070_read() computes the sensor value in ret but returns 0 instead of the actual result. This causes veml6070_read_raw() to always report 0. Return the computed value instead of 0. Running make W=1 returns no errors. I was unable to test the patch because I do not have the hardware. Found by code inspection. Fixes: fc38525135dd ("iio: light: veml6070: use guard to handle mutex") Signed-off-by: Aldo Conte Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6070.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 6d4483c85f30cf..74d7246e5225e9 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -134,9 +134,7 @@ static int veml6070_read(struct veml6070_data *data) if (ret < 0) return ret; - ret = (msb << 8) | lsb; - - return 0; + return (msb << 8) | lsb; } static const struct iio_chan_spec veml6070_channels[] = { From c720fb57d56274213d027b3c5ab99080cf62a306 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Tue, 24 Mar 2026 14:21:29 -0500 Subject: [PATCH 127/700] gpio: mxc: map Both Edge pad wakeup to Rising Edge Suspend may fail on i.MX8QM when Falling Edge is used as a pad wakeup trigger due to a hardware bug in the detection logic. Since the hardware does not support Both Edge wakeup, remap requests for Both Edge to Rising Edge by default to avoid hitting this issue. A warning is emitted when Falling Edge is selected on i.MX8QM. Fixes: f60c9eac54af ("gpio: mxc: enable pad wakeup on i.MX8x platforms") cc: stable@vger.kernel.org Reviewed-by: Peng Fan Signed-off-by: Shenwei Wang Link: https://patch.msgid.link/20260324192129.2797237-1-shenwei.wang@nxp.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index d7666fe9dbf86e..647b6f4861b744 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -584,12 +584,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) unsigned long config; bool ret = false; int i, type; + bool is_imx8qm = of_device_is_compatible(port->dev->of_node, "fsl,imx8qm-gpio"); static const u32 pad_type_map[] = { IMX_SCU_WAKEUP_OFF, /* 0 */ IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */ - IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */ + IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_BOTH */ IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */ IMX_SCU_WAKEUP_OFF, /* 5 */ IMX_SCU_WAKEUP_OFF, /* 6 */ @@ -604,6 +605,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) config = pad_type_map[type]; else config = IMX_SCU_WAKEUP_OFF; + + if (is_imx8qm && config == IMX_SCU_WAKEUP_FALL_EDGE) { + dev_warn_once(port->dev, + "No falling-edge support for wakeup on i.MX8QM\n"); + config = IMX_SCU_WAKEUP_OFF; + } + ret |= mxc_gpio_generic_config(port, i, config); } } From 4b56770d345524fc2acc143a2b85539cf7d74bc1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 16 Mar 2026 13:21:19 -0700 Subject: [PATCH 128/700] crypto: tegra - Add missing CRYPTO_ALG_ASYNC The tegra crypto driver failed to set the CRYPTO_ALG_ASYNC on its asynchronous algorithms, causing the crypto API to select them for users that request only synchronous algorithms. This causes crashes (at least). Fix this by adding the flag like what the other drivers do. Also remove the unnecessary CRYPTO_ALG_TYPE_* flags, since those just get ignored and overridden by the registration function anyway. Reported-by: Zorro Lang Closes: https://lore.kernel.org/r/20260314080937.pghb4aa7d4je3mhh@dell-per750-06-vm-08.rhts.eng.pek2.redhat.com Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Cc: stable@vger.kernel.org Cc: Akhil R Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu --- drivers/crypto/tegra/tegra-se-aes.c | 11 ++++++---- drivers/crypto/tegra/tegra-se-hash.c | 30 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 0e07d0523291a5..9210cceb4b7b2c 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -529,7 +529,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -550,7 +550,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "ecb(aes)", .cra_driver_name = "ecb-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -572,7 +572,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "ctr(aes)", .cra_driver_name = "ctr-aes-tegra", .cra_priority = 500, - .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = 0xf, @@ -594,6 +594,7 @@ static struct tegra_se_alg tegra_aes_algs[] = { .cra_name = "xts(aes)", .cra_driver_name = "xts-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_aes_ctx), .cra_alignmask = (__alignof__(u64) - 1), @@ -1922,6 +1923,7 @@ static struct tegra_se_alg tegra_aead_algs[] = { .cra_name = "gcm(aes)", .cra_driver_name = "gcm-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aead_ctx), .cra_alignmask = 0xf, @@ -1944,6 +1946,7 @@ static struct tegra_se_alg tegra_aead_algs[] = { .cra_name = "ccm(aes)", .cra_driver_name = "ccm-aes-tegra", .cra_priority = 500, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct tegra_aead_ctx), .cra_alignmask = 0xf, @@ -1971,7 +1974,7 @@ static struct tegra_se_alg tegra_cmac_algs[] = { .cra_name = "cmac(aes)", .cra_driver_name = "tegra-se-cmac", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_cmac_ctx), .cra_alignmask = 0, diff --git a/drivers/crypto/tegra/tegra-se-hash.c b/drivers/crypto/tegra/tegra-se-hash.c index 4a298ace6e9f79..06bb5bf0fa335c 100644 --- a/drivers/crypto/tegra/tegra-se-hash.c +++ b/drivers/crypto/tegra/tegra-se-hash.c @@ -761,7 +761,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha1", .cra_driver_name = "tegra-se-sha1", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -786,7 +786,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha224", .cra_driver_name = "tegra-se-sha224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -811,7 +811,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha256", .cra_driver_name = "tegra-se-sha256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -836,7 +836,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha384", .cra_driver_name = "tegra-se-sha384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -861,7 +861,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha512", .cra_driver_name = "tegra-se-sha512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -886,7 +886,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-224", .cra_driver_name = "tegra-se-sha3-224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -911,7 +911,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-256", .cra_driver_name = "tegra-se-sha3-256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -936,7 +936,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-384", .cra_driver_name = "tegra-se-sha3-384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -961,7 +961,7 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "sha3-512", .cra_driver_name = "tegra-se-sha3-512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = SHA3_512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -988,7 +988,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha224)", .cra_driver_name = "tegra-se-hmac-sha224", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1015,7 +1016,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha256)", .cra_driver_name = "tegra-se-hmac-sha256", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1042,7 +1044,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha384)", .cra_driver_name = "tegra-se-hmac-sha384", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, @@ -1069,7 +1072,8 @@ static struct tegra_se_alg tegra_hash_algs[] = { .cra_name = "hmac(sha512)", .cra_driver_name = "tegra-se-hmac-sha512", .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_sha_ctx), .cra_alignmask = 0, From 5ddfdcbe10dc5f97afc4e46ca22be2be717e8caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horia=20Geant=C4=83?= Date: Tue, 17 Mar 2026 12:25:13 +0200 Subject: [PATCH 129/700] crypto: caam - fix DMA corruption on long hmac keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a key longer than block size is supplied, it is copied and then hashed into the real key. The memory allocated for the copy needs to be rounded to DMA cache alignment, as otherwise the hashed key may corrupt neighbouring memory. The rounding was performed, but never actually used for the allocation. Fix this by replacing kmemdup with kmalloc for a larger buffer, followed by memcpy. Fixes: 199354d7fb6e ("crypto: caam - Remove GFP_DMA and add DMA alignment padding") Reported-by: Paul Bunyan Signed-off-by: Horia Geantă Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamhash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 628c43a7efc41f..44122208f70cb9 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -441,9 +441,10 @@ static int ahash_setkey(struct crypto_ahash *ahash, if (aligned_len < keylen) return -EOVERFLOW; - hashed_key = kmemdup(key, keylen, GFP_KERNEL); + hashed_key = kmalloc(aligned_len, GFP_KERNEL); if (!hashed_key) return -ENOMEM; + memcpy(hashed_key, key, keylen); ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize); if (ret) goto bad_free_key; From 80688afb9c35b3934ce2d6be9973758915e2e0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horia=20Geant=C4=83?= Date: Tue, 17 Mar 2026 12:25:14 +0200 Subject: [PATCH 130/700] crypto: caam - fix overflow on long hmac keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a key longer than block size is supplied, it is copied and then hashed into the real key. The memory allocated for the copy needs to be rounded to DMA cache alignment, as otherwise the hashed key may corrupt neighbouring memory. The copying is performed using kmemdup, however this leads to an overflow: reading more bytes (aligned_len - keylen) from the keylen source buffer. Fix this by replacing kmemdup with kmalloc, followed by memcpy. Fixes: 199354d7fb6e ("crypto: caam - Remove GFP_DMA and add DMA alignment padding") Signed-off-by: Horia Geantă Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamalg_qi2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index 167372936ca700..78964e1712e581 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -3326,9 +3326,10 @@ static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key, if (aligned_len < keylen) return -EOVERFLOW; - hashed_key = kmemdup(key, aligned_len, GFP_KERNEL); + hashed_key = kmalloc(aligned_len, GFP_KERNEL); if (!hashed_key) return -ENOMEM; + memcpy(hashed_key, key, keylen); ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize); if (ret) goto bad_free_key; From 6d89f743e57cb34e233a8217b394c7ee09abf225 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 25 Mar 2026 16:31:38 +0100 Subject: [PATCH 131/700] crypto: deflate - fix spurious -ENOSPC The code in deflate_decompress_one may erroneously return -ENOSPC even if it didn't run out of output space. The error happens under this condition: - Suppose that there are two input pages, the compressed data fits into the first page and the zlib checksum is placed in the second page. - The code iterates over the first page, decompresses the data and fully fills the destination buffer, zlib_inflate returns Z_OK becuse zlib hasn't seen the checksum yet. - The outer do-while loop is iterated again, acomp_walk_next_src sets the input parameters to the second page containing the checksum. - We go into the inner do-while loop, execute "dcur = acomp_walk_next_dst(&walk);". "dcur" is zero, so we break out of the loop and return -ENOSPC, despite the fact that the decompressed data fit into the destination buffer. In order to fix this bug, this commit changes the logic when to report the -ENOSPC error. We report the error if the destination buffer is empty *and* if zlib_inflate didn't make any progress consuming the input buffer. If zlib_inflate consumes the trailing checksum, we see that it made progress and we will not return -ENOSPC. Fixes: 08cabc7d3c86 ("crypto: deflate - Convert to acomp") Signed-off-by: Mikulas Patocka Signed-off-by: Herbert Xu --- crypto/deflate.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crypto/deflate.c b/crypto/deflate.c index 46fc7def8d4cc8..710ebba7ce850f 100644 --- a/crypto/deflate.c +++ b/crypto/deflate.c @@ -164,18 +164,21 @@ static int deflate_decompress_one(struct acomp_req *req, do { unsigned int dcur; + unsigned long avail_in; dcur = acomp_walk_next_dst(&walk); - if (!dcur) { - out_of_space = true; - break; - } stream->avail_out = dcur; stream->next_out = walk.dst.virt.addr; + avail_in = stream->avail_in; ret = zlib_inflate(stream, Z_NO_FLUSH); + if (!dcur && avail_in == stream->avail_in) { + out_of_space = true; + break; + } + dcur -= stream->avail_out; acomp_walk_done_dst(&walk, dcur); } while (ret == Z_OK && stream->avail_in); From 62397b493e14107ae82d8b80938f293d95425bcb Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Wed, 25 Mar 2026 18:26:13 +0100 Subject: [PATCH 132/700] crypto: af-alg - fix NULL pointer dereference in scatterwalk The AF_ALG interface fails to unmark the end of a Scatter/Gather List (SGL) when chaining a new af_alg_tsgl structure. If a sendmsg() fills an SGL exactly to MAX_SGL_ENTS, the last entry is marked as the end. A subsequent sendmsg() allocates a new SGL and chains it, but fails to clear the end marker on the previous SGL's last data entry. This causes the crypto scatterwalk to hit a premature end, returning NULL on sg_next() and leading to a kernel panic during dereference. Fix this by explicitly unmarking the end of the previous SGL when performing sg_chain() in af_alg_alloc_tsgl(). Fixes: 8ff590903d5f ("crypto: algif_skcipher - User-space interface for skcipher operations") Signed-off-by: Norbert Szetei Signed-off-by: Herbert Xu --- crypto/af_alg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 0bb609fbec7d9e..c2fd9cd86c5efa 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -623,8 +623,10 @@ static int af_alg_alloc_tsgl(struct sock *sk) sg_init_table(sgl->sg, MAX_SGL_ENTS + 1); sgl->cur = 0; - if (sg) + if (sg) { + sg_unmark_end(sg + MAX_SGL_ENTS - 1); sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg); + } list_add_tail(&sgl->list, &ctx->tsgl_list); } From f078634c184a9b5ccaa056e8b8d6cd32f7bff1b6 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Wed, 18 Mar 2026 13:26:05 +0800 Subject: [PATCH 133/700] drm/bridge: Fix refcount shown via debugfs for encoder_bridges_show() A typical bridge refcount value is 3 after a bridge chain is formed: - devm_drm_bridge_alloc() initializes the refcount value to be 1. - drm_bridge_add() gets an additional reference hence 2. - drm_bridge_attach() gets the third reference hence 3. This typical refcount value aligns with allbridges_show()'s behaviour. However, since encoder_bridges_show() uses drm_for_each_bridge_in_chain_scoped() to automatically get/put the bridge reference while iterating, a bogus reference is accidentally got when showing the wrong typical refcount value as 4 to users via debugfs. Fix this by caching the refcount value returned from kref_read() while iterating and explicitly decreasing the cached refcount value by 1 before showing it to users. Fixes: bd57048e4576 ("drm/bridge: use drm_for_each_bridge_in_chain_scoped()") Signed-off-by: Liu Ying Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli Link: https://patch.msgid.link/20260318-drm-misc-next-2026-03-05-fix-encoder-bridges-refcount-v3-1-147fea581279@nxp.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/drm_bridge.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index d6f11c68bb6a44..1987789b258a1e 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1569,11 +1569,17 @@ EXPORT_SYMBOL(devm_drm_put_bridge); static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx, - bool lingering) + bool lingering, + bool scoped) { + unsigned int refcount = kref_read(&bridge->refcount); + + if (scoped) + refcount--; + drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); - drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount), + drm_printf(p, "\trefcount: %u%s\n", refcount, lingering ? " [lingering]" : ""); drm_printf(p, "\ttype: [%d] %s\n", @@ -1607,10 +1613,10 @@ static int allbridges_show(struct seq_file *m, void *data) mutex_lock(&bridge_lock); list_for_each_entry(bridge, &bridge_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, false); list_for_each_entry(bridge, &bridge_lingering_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true, false); mutex_unlock(&bridge_lock); @@ -1625,7 +1631,7 @@ static int encoder_bridges_show(struct seq_file *m, void *data) unsigned int idx = 0; drm_for_each_bridge_in_chain_scoped(encoder, bridge) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, true); return 0; } From c7596f9001e2b83293e3658e4e1addde69bb335d Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 3 Mar 2026 11:24:06 +0000 Subject: [PATCH 134/700] firmware: microchip: fail auto-update probe if no flash found There's no point letting the driver probe if there is no flash, as trying to do a firmware upload will fail. Move the code that attempts to get the flash from firmware upload to probe, and let it emit a message to users stating why auto-update is not supported. The code currently could have a problem if there's a flash in devicetree, but the system controller driver fails to get a pointer to it from the mtd subsystem, which will cause mpfs_sys_controller_get_flash() to return an error. Check for errors and null, instead of just null, in the new clause. CC: stable@vger.kernel.org Fixes: ec5b0f1193ad4 ("firmware: microchip: add PolarFire SoC Auto Update support") Signed-off-by: Conor Dooley --- drivers/firmware/microchip/mpfs-auto-update.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c index e194f7acb2a9b0..8fc3749d4a7098 100644 --- a/drivers/firmware/microchip/mpfs-auto-update.c +++ b/drivers/firmware/microchip/mpfs-auto-update.c @@ -113,10 +113,6 @@ static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader * be added here. */ - priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller); - if (!priv->flash) - return FW_UPLOAD_ERR_HW_ERROR; - erase_size = round_up(erase_size, (u64)priv->flash->erasesize); /* @@ -427,6 +423,12 @@ static int mpfs_auto_update_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->sys_controller), "Could not register as a sub device of the system controller\n"); + priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller); + if (IS_ERR_OR_NULL(priv->flash)) { + dev_dbg(dev, "No flash connected to the system controller, auto-update not supported\n"); + return -ENODEV; + } + priv->dev = dev; platform_set_drvdata(pdev, priv); From 45ebe43ea00d6b9f5b3e0db9c35b8ca2a96b7e70 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 26 Mar 2026 08:07:29 +0100 Subject: [PATCH 135/700] Revert "drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6bee098b91417654703e17eb5c1822c6dfd0c01d. Den 2026-03-25 kl. 22:11, skrev Simona Vetter: > On Wed, Mar 25, 2026 at 10:26:40AM -0700, Guenter Roeck wrote: >> Hi, >> >> On Fri, Mar 13, 2026 at 04:17:27PM +0100, Maarten Lankhorst wrote: >>> When trying to do a rather aggressive test of igt's "xe_module_load >>> --r reload" with a full desktop environment and game running I noticed >>> a few OOPSes when dereferencing freed pointers, related to >>> framebuffers and property blobs after the compositor exits. >>> >>> Solve this by guarding the freeing in drm_file with drm_dev_enter/exit, >>> and immediately put the references from struct drm_file objects during >>> drm_dev_unplug(). >>> >> >> With this patch in v6.18.20, I get the warning backtraces below. >> The backtraces are gone with the patch reverted. > > Yeah, this needs to be reverted, reasoning below. Maarten, can you please > take care of that and feed the revert through the usual channels? I don't > think it's critical enough that we need to fast-track this into drm.git > directly. > > Quoting the patch here again: > >> drivers/gpu/drm/drm_file.c | 5 ++++- >> drivers/gpu/drm/drm_mode_config.c | 9 ++++++--- >> 2 files changed, 10 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c >> index ec820686b3021..f52141f842a1f 100644 >> --- a/drivers/gpu/drm/drm_file.c >> +++ b/drivers/gpu/drm/drm_file.c >> @@ -233,6 +233,7 @@ static void drm_events_release(struct drm_file *file_priv) >> void drm_file_free(struct drm_file *file) >> { >> struct drm_device *dev; >> + int idx; >> >> if (!file) >> return; >> @@ -249,9 +250,11 @@ void drm_file_free(struct drm_file *file) >> >> drm_events_release(file); >> >> - if (drm_core_check_feature(dev, DRIVER_MODESET)) { >> + if (drm_core_check_feature(dev, DRIVER_MODESET) && >> + drm_dev_enter(dev, &idx)) { > > This is misplaced for two reasons: > > - Even if we'd want to guarantee that we hold a drm_dev_enter/exit > reference during framebuffer teardown, we'd need to do this > _consistently over all callsites. Not ad-hoc in just one place that a > testcase hits. This also means kerneldoc updates of the relevant hooks > and at least a bunch of acks from other driver people to document the > consensus. > > - More importantly, this is driver responsibilities in general unless we > have extremely good reasons to the contrary. Which means this must be > placed in xe. > >> drm_fb_release(file); >> drm_property_destroy_user_blobs(dev, file); >> + drm_dev_exit(idx); >> } >> >> if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) >> diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c >> index 84ae8a23a3678..e349418978f79 100644 >> --- a/drivers/gpu/drm/drm_mode_config.c >> +++ b/drivers/gpu/drm/drm_mode_config.c >> @@ -583,10 +583,13 @@ void drm_mode_config_cleanup(struct drm_device *dev) >> */ >> WARN_ON(!list_empty(&dev->mode_config.fb_list)); >> list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { >> - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); >> + if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { >> + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); > > This is also wrong: > > - Firstly, it's a completely independent bug, we do not smash two bugfixes > into one patch. > > - Secondly, it's again a driver bug: drm_mode_cleanup must be called when > the last drm_device reference disappears (hence the existence of > drmm_mode_config_init), not when the driver gets unbound. The fact that > this shows up in a callchain from a devres cleanup means the intel > driver gets this wrong (like almost everyone else because historically > we didn't know better). > > If we don't follow this rule, then we get races with this code here > running concurrently with drm_file fb cleanups, which just does not > work. Review pointed that out, but then shrugged it off with a confused > explanation: > > https://lore.kernel.org/all/e61e64c796ccfb17ae673331a3df4b877bf42d82.camel@linux.intel.com/ > > Yes this also means a lot of the other drm_device teardown that drivers > do happens way too early. There is a massive can of worms here of a > magnitude that most likely is much, much bigger than what you can > backport to stable kernels. Hotunplug is _hard_. Back to the drawing board, and fixing it in the intel display driver instead. Cc: Thomas Hellström Fixes: 6bee098b9141 ("drm: Fix use-after-free on framebuffers and property blobs when calling drm_dev_unplug") Reported-by: Guenter Roeck Tested-by: Guenter Roeck Acked-by: Simona Vetter Signed-off-by: Maarten Lankhorst Link: https://patch.msgid.link/20260326082217.39941-2-dev@lankhorst.se --- drivers/gpu/drm/drm_file.c | 5 +---- drivers/gpu/drm/drm_mode_config.c | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index f52141f842a1f4..ec820686b30216 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -233,7 +233,6 @@ static void drm_events_release(struct drm_file *file_priv) void drm_file_free(struct drm_file *file) { struct drm_device *dev; - int idx; if (!file) return; @@ -250,11 +249,9 @@ void drm_file_free(struct drm_file *file) drm_events_release(file); - if (drm_core_check_feature(dev, DRIVER_MODESET) && - drm_dev_enter(dev, &idx)) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) { drm_fb_release(file); drm_property_destroy_user_blobs(dev, file); - drm_dev_exit(idx); } if (drm_core_check_feature(dev, DRIVER_SYNCOBJ)) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 802bc4608abf54..d12db9b0bab810 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -577,13 +577,10 @@ void drm_mode_config_cleanup(struct drm_device *dev) */ WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { - if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) { - struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); + struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]"); - drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); - drm_framebuffer_print_info(&p, 1, fb); - } - list_del_init(&fb->filp_head); + drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); + drm_framebuffer_print_info(&p, 1, fb); drm_framebuffer_free(&fb->base.refcount); } From 88bdac5443e5269bb39c4968d5ee0becbffe3f82 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 25 Mar 2026 13:22:10 +0100 Subject: [PATCH 136/700] dt-bindings: display/msm: qcm2290-mdss: Fix missing ranges in example Device node has children with MMIO addressing, so must have ranges: msm/qcom,qcm2290-mdss.example.dtb: display-subsystem@5e00000 (qcom,qcm2290-mdss): 'ranges' is a required property Fixes: 966a08c293cb ("dt-bindings: display: msm: qcm2290-mdss: Fix iommus property") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sumit Garg Link: https://lore.kernel.org/r/20260325122209.147128-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml index 2772cdec7e42ab..bb09ecd1a5b4ff 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml @@ -108,6 +108,7 @@ examples: "cpu-cfg"; iommus = <&apps_smmu 0x420 0x2>; + ranges; display-controller@5e01000 { compatible = "qcom,qcm2290-dpu"; From 629ec78ef8608d955ce217880cdc3e1873af3a15 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 24 Mar 2026 00:25:57 +0100 Subject: [PATCH 137/700] mpls: add seqcount to protect the platform_label{,s} pair The RCU-protected codepaths (mpls_forward, mpls_dump_routes) can have an inconsistent view of platform_labels vs platform_label in case of a concurrent resize (resize_platform_label_table, under platform_mutex). This can lead to OOB accesses. This patch adds a seqcount, so that we get a consistent snapshot. Note that mpls_label_ok is also susceptible to this, so the check against RTA_DST in rtm_to_route_config, done outside platform_mutex, is not sufficient. This value gets passed to mpls_label_ok once more in both mpls_route_add and mpls_route_del, so there is no issue, but that additional check must not be removed. Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Fixes: 7720c01f3f590 ("mpls: Add a sysctl to control the size of the mpls label table") Fixes: dde1b38e873c ("mpls: Convert mpls_dump_routes() to RCU.") Signed-off-by: Sabrina Dubroca Link: https://patch.msgid.link/cd8fca15e3eb7e212b094064cd83652e20fd9d31.1774284088.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski --- include/net/netns/mpls.h | 1 + net/mpls/af_mpls.c | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h index 6682e51513efa5..2073cbac2afb51 100644 --- a/include/net/netns/mpls.h +++ b/include/net/netns/mpls.h @@ -17,6 +17,7 @@ struct netns_mpls { size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label; struct mutex platform_mutex; + seqcount_mutex_t platform_label_seq; struct ctl_table_header *ctl; }; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index d5417688f69e63..18d3da8ab38487 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -83,14 +83,30 @@ static struct mpls_route *mpls_route_input(struct net *net, unsigned int index) return mpls_dereference(net, platform_label[index]); } +static struct mpls_route __rcu **mpls_platform_label_rcu(struct net *net, size_t *platform_labels) +{ + struct mpls_route __rcu **platform_label; + unsigned int sequence; + + do { + sequence = read_seqcount_begin(&net->mpls.platform_label_seq); + platform_label = rcu_dereference(net->mpls.platform_label); + *platform_labels = net->mpls.platform_labels; + } while (read_seqcount_retry(&net->mpls.platform_label_seq, sequence)); + + return platform_label; +} + static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned int index) { struct mpls_route __rcu **platform_label; + size_t platform_labels; + + platform_label = mpls_platform_label_rcu(net, &platform_labels); - if (index >= net->mpls.platform_labels) + if (index >= platform_labels) return NULL; - platform_label = rcu_dereference(net->mpls.platform_label); return rcu_dereference(platform_label[index]); } @@ -2240,8 +2256,7 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) if (index < MPLS_LABEL_FIRST_UNRESERVED) index = MPLS_LABEL_FIRST_UNRESERVED; - platform_label = rcu_dereference(net->mpls.platform_label); - platform_labels = net->mpls.platform_labels; + platform_label = mpls_platform_label_rcu(net, &platform_labels); if (filter.filter_set) flags |= NLM_F_DUMP_FILTERED; @@ -2645,8 +2660,12 @@ static int resize_platform_label_table(struct net *net, size_t limit) } /* Update the global pointers */ + local_bh_disable(); + write_seqcount_begin(&net->mpls.platform_label_seq); net->mpls.platform_labels = limit; rcu_assign_pointer(net->mpls.platform_label, labels); + write_seqcount_end(&net->mpls.platform_label_seq); + local_bh_enable(); mutex_unlock(&net->mpls.platform_mutex); @@ -2728,6 +2747,8 @@ static __net_init int mpls_net_init(struct net *net) int i; mutex_init(&net->mpls.platform_mutex); + seqcount_mutex_init(&net->mpls.platform_label_seq, &net->mpls.platform_mutex); + net->mpls.platform_labels = 0; net->mpls.platform_label = NULL; net->mpls.ip_ttl_propagate = 1; From f73896b4197ed53cf0894657c899265ef7c86b7a Mon Sep 17 00:00:00 2001 From: Dipayaan Roy Date: Tue, 24 Mar 2026 11:14:28 -0700 Subject: [PATCH 138/700] net: mana: Fix RX skb truesize accounting MANA passes rxq->alloc_size to napi_build_skb() for all RX buffers. It is correct for fragment-backed RX buffers, where alloc_size matches the actual backing allocation used for each packet buffer. However, in the non-fragment RX path mana allocates a full page, or a higher-order page, per RX buffer. In that case alloc_size only reflects the usable packet area and not the actual backing memory. This causes napi_build_skb() to underestimate the skb backing allocation in the single-buffer RX path, so skb->truesize is derived from a value smaller than the real RX buffer allocation. Fix this by updating alloc_size in the non-fragment RX path to the actual backing allocation size before it is passed to napi_build_skb(). Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") Signed-off-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/acLUhLpLum6qrD/N@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microsoft/mana/mana_en.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index dca62fb9a3a9ef..09a53c97754550 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -766,6 +766,13 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc, } *frag_count = 1; + + /* In the single-buffer path, napi_build_skb() must see the + * actual backing allocation size so skb->truesize reflects + * the full page (or higher-order page), not just the usable + * packet area. + */ + *alloc_size = PAGE_SIZE << get_order(*alloc_size); return; } From 976ff48c2ac6e6b25b01428c9d7997bcd0fb2949 Mon Sep 17 00:00:00 2001 From: "Sven Eckelmann (Plasma Cloud)" Date: Tue, 24 Mar 2026 09:36:01 +0100 Subject: [PATCH 139/700] net: ethernet: mtk_ppe: avoid NULL deref when gmac0 is disabled If the gmac0 is disabled, the precheck for a valid ingress device will cause a NULL pointer deref and crash the system. This happens because eth->netdev[0] will be NULL but the code will directly try to access netdev_ops. Instead of just checking for the first net_device, it must be checked if any of the mtk_eth net_devices is matching the netdev_ops of the ingress device. Cc: stable@vger.kernel.org Fixes: 73cfd947dbdb ("net: ethernet: mtk_eth_soc: ppe: prevent ppe update for non-mtk devices") Signed-off-by: Sven Eckelmann (Plasma Cloud) Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324-wed-crash-gmac0-disabled-v1-1-3bc388aee565@simonwunderlich.de Signed-off-by: Jakub Kicinski --- .../net/ethernet/mediatek/mtk_ppe_offload.c | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index cb30108f2bf67e..cc8c4ef8038f30 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -244,6 +244,25 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, return 0; } +static bool +mtk_flow_is_valid_idev(const struct mtk_eth *eth, const struct net_device *idev) +{ + size_t i; + + if (!idev) + return false; + + for (i = 0; i < ARRAY_SIZE(eth->netdev); i++) { + if (!eth->netdev[i]) + continue; + + if (idev->netdev_ops == eth->netdev[i]->netdev_ops) + return true; + } + + return false; +} + static int mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f, int ppe_index) @@ -270,7 +289,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f, flow_rule_match_meta(rule, &match); if (mtk_is_netsys_v2_or_greater(eth)) { idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex); - if (idev && idev->netdev_ops == eth->netdev[0]->netdev_ops) { + if (mtk_flow_is_valid_idev(eth, idev)) { struct mtk_mac *mac = netdev_priv(idev); if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num)) From 57a04a13aac1f247d171c3f3aef93efc69e6979e Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 24 Mar 2026 22:08:56 +0800 Subject: [PATCH 140/700] netdevsim: fix build if SKB_EXTENSIONS=n __skb_ext_put() is not declared if SKB_EXTENSIONS is not enabled, which causes a build error: drivers/net/netdevsim/netdev.c: In function 'nsim_forward_skb': drivers/net/netdevsim/netdev.c:114:25: error: implicit declaration of function '__skb_ext_put'; did you mean 'skb_ext_put'? [-Werror=implicit-function-declaration] 114 | __skb_ext_put(psp_ext); | ^~~~~~~~~~~~~ | skb_ext_put cc1: some warnings being treated as errors Add a stub to fix the build. Fixes: 7d9351435ebb ("netdevsim: drop PSP ext ref on forward failure") Signed-off-by: Qingfang Deng Link: https://patch.msgid.link/20260324140857.783-1-dqfext@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/skbuff.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index daa4e4944ce3f2..2f278ce376b7ed 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -5097,6 +5097,7 @@ static inline bool skb_has_extensions(struct sk_buff *skb) return unlikely(skb->active_extensions); } #else +static inline void __skb_ext_put(struct skb_ext *ext) {} static inline void skb_ext_put(struct sk_buff *skb) {} static inline void skb_ext_reset(struct sk_buff *skb) {} static inline void skb_ext_del(struct sk_buff *skb, int unused) {} From e8e44c98f789dee45cfd24ffb9d4936e0606d7c6 Mon Sep 17 00:00:00 2001 From: Buday Csaba Date: Tue, 24 Mar 2026 14:32:30 +0100 Subject: [PATCH 141/700] net: fec: fix the PTP periodic output sysfs interface When the PPS channel configuration was implemented, the channel index for the periodic outputs was configured as the hardware channel number. The sysfs interface uses a logical channel index, and rejects numbers greater than `n_per_out` (see period_store() in ptp_sysfs.c). That property was left at 1, since the driver implements channel selection, not simultaneous operation of multiple PTP hardware timer channels. A second check in fec_ptp_enable() returns -EOPNOTSUPP when the two channel numbers disagree, making channels 1..3 unusable from sysfs. Fix by removing this redundant check in the FEC PTP driver. Fixes: 566c2d83887f ("net: fec: make PPS channel configurable") Signed-off-by: Buday Csaba Link: https://patch.msgid.link/8ec2afe88423c2231f9cf8044d212ce57846670e.1774359059.git.buday.csaba@prolan.hu Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fec_ptp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 4b7bad9a485df2..56801c2009d59a 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -545,9 +545,6 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, if (rq->perout.flags) return -EOPNOTSUPP; - if (rq->perout.index != fep->pps_channel) - return -EOPNOTSUPP; - period.tv_sec = rq->perout.period.sec; period.tv_nsec = rq->perout.period.nsec; period_ns = timespec64_to_ns(&period); From 0239fd701d33475a39428daa3dc627407cd417a6 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:19 +0800 Subject: [PATCH 142/700] net: enetc: reset PIR and CIR if they are not equal when initializing TX ring Currently the driver does not reset the producer index register (PIR) and consumer index register (CIR) when initializing a TX BD ring. The driver only reads the PIR and CIR and initializes the software indexes. If the TX BD ring is reinitialized when it still contains unsent frames, its PIR and CIR will not be equal after the reinitialization. However, the BDs between CIR and PIR have been freed and become invalid and this can lead to a hardware malfunction, causing the TX BD ring will not work properly. For ENETC v4, it supports software to set the PIR and CIR, so the driver can reset these two registers if they are not equal when reinitializing the TX BD ring. Therefore, add this solution for ENETC v4. Note that this patch does not work for ENETC v1 because it does not support software to set the PIR and CIR. Fixes: 99100d0d9922 ("net: enetc: add preliminary support for i.MX95 ENETC PF") Signed-off-by: Wei Fang Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-2-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index a146ceaf2ed628..aa8a87124b1032 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -2578,6 +2578,7 @@ EXPORT_SYMBOL_GPL(enetc_free_si_resources); static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring) { + struct enetc_si *si = container_of(hw, struct enetc_si, hw); int idx = tx_ring->index; u32 tbmr; @@ -2591,10 +2592,20 @@ static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring) enetc_txbdr_wr(hw, idx, ENETC_TBLENR, ENETC_RTBLENR_LEN(tx_ring->bd_count)); - /* clearing PI/CI registers for Tx not supported, adjust sw indexes */ + /* For ENETC v1, clearing PI/CI registers for Tx not supported, + * adjust sw indexes + */ tx_ring->next_to_use = enetc_txbdr_rd(hw, idx, ENETC_TBPIR); tx_ring->next_to_clean = enetc_txbdr_rd(hw, idx, ENETC_TBCIR); + if (tx_ring->next_to_use != tx_ring->next_to_clean && + !is_enetc_rev1(si)) { + tx_ring->next_to_use = 0; + tx_ring->next_to_clean = 0; + enetc_txbdr_wr(hw, idx, ENETC_TBPIR, 0); + enetc_txbdr_wr(hw, idx, ENETC_TBCIR, 0); + } + /* enable Tx ints by setting pkt thr to 1 */ enetc_txbdr_wr(hw, idx, ENETC_TBICR0, ENETC_TBICR0_ICEN | 0x1); From 2725d84efe2582c0a4b907e74a689d26b2dbd382 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:20 +0800 Subject: [PATCH 143/700] net: enetc: add graceful stop to safely reinitialize the TX Ring For ENETC v4, the PIR and CIR will be reset if they are not equal when reinitializing the TX BD ring. However, resetting the PIR and CIR alone is insufficient. When a link-down event occurs while the TX BD ring is transmitting frames, subsequent reinitialization of the TX BD ring may cause it to malfunction. For example, the below steps can reproduce the problem. 1. Unplug the cable when the TX BD ring is busy transmitting frames. 2. Disable the network interface (ifconfig eth0 down). 3. Re-enable the network interface (ifconfig eth0 up). 4. Plug in the cable, the TX BD ring may fail to transmit packets. When the link-down event occurs, enetc4_pl_mac_link_down() only clears PMa_COMMAND_CONFIG[TX_EN] to disable MAC transmit data path. It doesn't set PORT[TXDIS] to 1 to flush the TX BD ring. Therefore, reinitializing the TX BD ring at this point is unsafe. To safely reinitialize the TX BD ring after a link-down event, we checked with the NETC IP team, a proper Ethernet MAC graceful stop is necessary. Therefore, add the Ethernet MAC graceful stop to the link-down event handler enetc4_pl_mac_link_down(). Fixes: 99100d0d9922 ("net: enetc: add preliminary support for i.MX95 ENETC PF") Signed-off-by: Wei Fang Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-3-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/freescale/enetc/enetc4_hw.h | 11 ++ .../net/ethernet/freescale/enetc/enetc4_pf.c | 111 +++++++++++++++--- 2 files changed, 108 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h index 3ed0f7a0276798..719c88ceb801a1 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h @@ -134,6 +134,12 @@ /* Port operational register */ #define ENETC4_POR 0x4100 +#define POR_TXDIS BIT(0) +#define POR_RXDIS BIT(1) + +/* Port status register */ +#define ENETC4_PSR 0x4104 +#define PSR_RX_BUSY BIT(1) /* Port traffic class a transmit maximum SDU register */ #define ENETC4_PTCTMSDUR(a) ((a) * 0x20 + 0x4208) @@ -173,6 +179,11 @@ /* Port internal MDIO base address, use to access PCS */ #define ENETC4_PM_IMDIO_BASE 0x5030 +/* Port MAC 0/1 Interrupt Event Register */ +#define ENETC4_PM_IEVENT(mac) (0x5040 + (mac) * 0x400) +#define PM_IEVENT_TX_EMPTY BIT(5) +#define PM_IEVENT_RX_EMPTY BIT(6) + /* Port MAC 0/1 Pause Quanta Register */ #define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 689b9f13c5eb34..53cecbb23a974a 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -444,20 +444,11 @@ static void enetc4_set_trx_frame_size(struct enetc_pf *pf) enetc4_pf_reset_tc_msdu(&si->hw); } -static void enetc4_enable_trx(struct enetc_pf *pf) -{ - struct enetc_hw *hw = &pf->si->hw; - - /* Enable port transmit/receive */ - enetc_port_wr(hw, ENETC4_POR, 0); -} - static void enetc4_configure_port(struct enetc_pf *pf) { enetc4_configure_port_si(pf); enetc4_set_trx_frame_size(pf); enetc_set_default_rss_key(pf); - enetc4_enable_trx(pf); } static int enetc4_init_ntmp_user(struct enetc_si *si) @@ -801,15 +792,105 @@ static void enetc4_set_tx_pause(struct enetc_pf *pf, int num_rxbdr, bool tx_paus enetc_port_wr(hw, ENETC4_PPAUOFFTR, pause_off_thresh); } -static void enetc4_enable_mac(struct enetc_pf *pf, bool en) +static void enetc4_mac_wait_tx_empty(struct enetc_si *si, int mac) +{ + u32 val; + + if (read_poll_timeout(enetc_port_rd, val, + val & PM_IEVENT_TX_EMPTY, + 100, 10000, false, &si->hw, + ENETC4_PM_IEVENT(mac))) + dev_warn(&si->pdev->dev, + "MAC %d TX is not empty\n", mac); +} + +static void enetc4_mac_tx_graceful_stop(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + val = enetc_port_rd(hw, ENETC4_POR); + val |= POR_TXDIS; + enetc_port_wr(hw, ENETC4_POR, val); + + enetc4_mac_wait_tx_empty(si, 0); + if (si->hw_features & ENETC_SI_F_QBU) + enetc4_mac_wait_tx_empty(si, 1); + + val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); + val &= ~PM_CMD_CFG_TX_EN; + enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); +} + +static void enetc4_mac_tx_enable(struct enetc_pf *pf) { + struct enetc_hw *hw = &pf->si->hw; struct enetc_si *si = pf->si; u32 val; val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); - val &= ~(PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN); - val |= en ? (PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN) : 0; + val |= PM_CMD_CFG_TX_EN; + enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); + + val = enetc_port_rd(hw, ENETC4_POR); + val &= ~POR_TXDIS; + enetc_port_wr(hw, ENETC4_POR, val); +} + +static void enetc4_mac_wait_rx_empty(struct enetc_si *si, int mac) +{ + u32 val; + + if (read_poll_timeout(enetc_port_rd, val, + val & PM_IEVENT_RX_EMPTY, + 100, 10000, false, &si->hw, + ENETC4_PM_IEVENT(mac))) + dev_warn(&si->pdev->dev, + "MAC %d RX is not empty\n", mac); +} + +static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + if (si->hw_features & ENETC_SI_F_QBU) { + val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(1)); + val &= ~PM_CMD_CFG_RX_EN; + enetc_port_wr(hw, ENETC4_PM_CMD_CFG(1), val); + enetc4_mac_wait_rx_empty(si, 1); + } + + val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(0)); + val &= ~PM_CMD_CFG_RX_EN; + enetc_port_wr(hw, ENETC4_PM_CMD_CFG(0), val); + enetc4_mac_wait_rx_empty(si, 0); + + if (read_poll_timeout(enetc_port_rd, val, + !(val & PSR_RX_BUSY), + 100, 10000, false, hw, + ENETC4_PSR)) + dev_warn(&si->pdev->dev, "Port RX busy\n"); + + val = enetc_port_rd(hw, ENETC4_POR); + val |= POR_RXDIS; + enetc_port_wr(hw, ENETC4_POR, val); +} + +static void enetc4_mac_rx_enable(struct enetc_pf *pf) +{ + struct enetc_hw *hw = &pf->si->hw; + struct enetc_si *si = pf->si; + u32 val; + + val = enetc_port_rd(hw, ENETC4_POR); + val &= ~POR_RXDIS; + enetc_port_wr(hw, ENETC4_POR, val); + val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0)); + val |= PM_CMD_CFG_RX_EN; enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val); } @@ -853,7 +934,8 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config, enetc4_set_hd_flow_control(pf, hd_fc); enetc4_set_tx_pause(pf, priv->num_rx_rings, tx_pause); enetc4_set_rx_pause(pf, rx_pause); - enetc4_enable_mac(pf, true); + enetc4_mac_tx_enable(pf); + enetc4_mac_rx_enable(pf); } static void enetc4_pl_mac_link_down(struct phylink_config *config, @@ -862,7 +944,8 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config, { struct enetc_pf *pf = phylink_to_enetc_pf(config); - enetc4_enable_mac(pf, false); + enetc4_mac_rx_graceful_stop(pf); + enetc4_mac_tx_graceful_stop(pf); } static const struct phylink_mac_ops enetc_pl_mac_ops = { From f2df9567b123145a07ee4ea7440e233f5d0232cc Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 24 Mar 2026 14:21:21 +0800 Subject: [PATCH 144/700] net: enetc: do not access non-existent registers on pseudo MAC The ENETC4_PM_IEVENT and ENETC4_PM_CMD_CFG registers do not exist on the ENETC pseudo MAC, so the driver should prevent from accessing them. Fixes: 5175c1e4adca ("net: enetc: add basic support for the ENETC with pseudo MAC for i.MX94") Signed-off-by: Wei Fang Tested-by: Claudiu Manoil Reviewed-by: Claudiu Manoil Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324062121.2745033-4-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc4_pf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 53cecbb23a974a..56899f2254aaa2 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -814,6 +814,9 @@ static void enetc4_mac_tx_graceful_stop(struct enetc_pf *pf) val |= POR_TXDIS; enetc_port_wr(hw, ENETC4_POR, val); + if (enetc_is_pseudo_mac(si)) + return; + enetc4_mac_wait_tx_empty(si, 0); if (si->hw_features & ENETC_SI_F_QBU) enetc4_mac_wait_tx_empty(si, 1); @@ -856,6 +859,9 @@ static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) struct enetc_si *si = pf->si; u32 val; + if (enetc_is_pseudo_mac(si)) + goto check_rx_busy; + if (si->hw_features & ENETC_SI_F_QBU) { val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(1)); val &= ~PM_CMD_CFG_RX_EN; @@ -868,6 +874,7 @@ static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf) enetc_port_wr(hw, ENETC4_PM_CMD_CFG(0), val); enetc4_mac_wait_rx_empty(si, 0); +check_rx_busy: if (read_poll_timeout(enetc_port_rd, val, !(val & PSR_RX_BUSY), 100, 10000, false, hw, From 2428083101f6883f979cceffa76cd8440751ffe6 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 24 Mar 2026 16:06:44 +0800 Subject: [PATCH 145/700] net: qrtr: replace qrtr_tx_flow radix_tree with xarray to fix memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __radix_tree_create() allocates and links intermediate nodes into the tree one by one. If a subsequent allocation fails, the already-linked nodes remain in the tree with no corresponding leaf entry. These orphaned internal nodes are never reclaimed because radix_tree_for_each_slot() only visits slots containing leaf values. The radix_tree API is deprecated in favor of xarray. As suggested by Matthew Wilcox, migrate qrtr_tx_flow from radix_tree to xarray instead of fixing the radix_tree itself [1]. xarray properly handles cleanup of internal nodes — xa_destroy() frees all internal xarray nodes when the qrtr_node is released, preventing the leak. [1] https://lore.kernel.org/all/20260225071623.41275-1-jiayuan.chen@linux.dev/T/ Reported-by: syzbot+006987d1be3586e13555@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/000000000000bfba3a060bf4ffcf@google.com/T/ Fixes: 5fdeb0d372ab ("net: qrtr: Implement outgoing flow control") Signed-off-by: Jiayuan Chen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324080645.290197-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/qrtr/af_qrtr.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 55fd2dd375886f..d77e9c8212da51 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -118,7 +118,7 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports); * @ep: endpoint * @ref: reference count for node * @nid: node id - * @qrtr_tx_flow: tree of qrtr_tx_flow, keyed by node << 32 | port + * @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port * @qrtr_tx_lock: lock for qrtr_tx_flow inserts * @rx_queue: receive queue * @item: list item for broadcast list @@ -129,7 +129,7 @@ struct qrtr_node { struct kref ref; unsigned int nid; - struct radix_tree_root qrtr_tx_flow; + struct xarray qrtr_tx_flow; struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */ struct sk_buff_head rx_queue; @@ -172,6 +172,7 @@ static void __qrtr_node_release(struct kref *kref) struct qrtr_tx_flow *flow; unsigned long flags; void __rcu **slot; + unsigned long index; spin_lock_irqsave(&qrtr_nodes_lock, flags); /* If the node is a bridge for other nodes, there are possibly @@ -189,11 +190,9 @@ static void __qrtr_node_release(struct kref *kref) skb_queue_purge(&node->rx_queue); /* Free tx flow counters */ - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { - flow = *slot; - radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot); + xa_for_each(&node->qrtr_tx_flow, index, flow) kfree(flow); - } + xa_destroy(&node->qrtr_tx_flow); kfree(node); } @@ -228,9 +227,7 @@ static void qrtr_tx_resume(struct qrtr_node *node, struct sk_buff *skb) key = remote_node << 32 | remote_port; - rcu_read_lock(); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); + flow = xa_load(&node->qrtr_tx_flow, key); if (flow) { spin_lock(&flow->resume_tx.lock); flow->pending = 0; @@ -269,12 +266,13 @@ static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port, return 0; mutex_lock(&node->qrtr_tx_lock); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); + flow = xa_load(&node->qrtr_tx_flow, key); if (!flow) { flow = kzalloc_obj(*flow); if (flow) { init_waitqueue_head(&flow->resume_tx); - if (radix_tree_insert(&node->qrtr_tx_flow, key, flow)) { + if (xa_err(xa_store(&node->qrtr_tx_flow, key, flow, + GFP_KERNEL))) { kfree(flow); flow = NULL; } @@ -326,9 +324,7 @@ static void qrtr_tx_flow_failed(struct qrtr_node *node, int dest_node, unsigned long key = (u64)dest_node << 32 | dest_port; struct qrtr_tx_flow *flow; - rcu_read_lock(); - flow = radix_tree_lookup(&node->qrtr_tx_flow, key); - rcu_read_unlock(); + flow = xa_load(&node->qrtr_tx_flow, key); if (flow) { spin_lock_irq(&flow->resume_tx.lock); flow->tx_failed = 1; @@ -599,7 +595,7 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) node->nid = QRTR_EP_NID_AUTO; node->ep = ep; - INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL); + xa_init(&node->qrtr_tx_flow); mutex_init(&node->qrtr_tx_lock); qrtr_node_assign(node, nid); @@ -627,6 +623,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) struct qrtr_tx_flow *flow; struct sk_buff *skb; unsigned long flags; + unsigned long index; void __rcu **slot; mutex_lock(&node->ep_lock); @@ -649,10 +646,8 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep) /* Wake up any transmitters waiting for resume-tx from the node */ mutex_lock(&node->qrtr_tx_lock); - radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) { - flow = *slot; + xa_for_each(&node->qrtr_tx_flow, index, flow) wake_up_interruptible_all(&flow->resume_tx); - } mutex_unlock(&node->qrtr_tx_lock); qrtr_node_release(node); From ae05340ccaa9d347fe85415609e075545bec589f Mon Sep 17 00:00:00 2001 From: Yochai Eisenrich Date: Wed, 25 Mar 2026 00:49:25 +0200 Subject: [PATCH 146/700] net: ipv6: ndisc: fix ndisc_ra_useropt to initialize nduseropt_padX fields to zero to prevent an info-leak When processing Router Advertisements with user options the kernel builds an RTM_NEWNDUSEROPT netlink message. The nduseroptmsg struct has three padding fields that are never zeroed and can leak kernel data The fix is simple, just zeroes the padding fields. Fixes: 31910575a9de ("[IPv6]: Export userland ND options through netlink (RDNSS support)") Signed-off-by: Yochai Eisenrich Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324224925.2437775-1-echelonh@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ndisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f6a5d8c73af972..186e60c792145c 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1209,6 +1209,9 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type; ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code; ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3; + ndmsg->nduseropt_pad1 = 0; + ndmsg->nduseropt_pad2 = 0; + ndmsg->nduseropt_pad3 = 0; memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3); From 90c5def10bea574b101b7a520c015ca81742183f Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 2 Mar 2026 18:22:52 -0400 Subject: [PATCH 147/700] iommu: Do not call drivers for empty gathers An empty gather is coded with start=U64_MAX, end=0 and several drivers go on to convert that to a size with: end - start + 1 Which gives 2 for an empty gather. This then causes Weird Stuff to happen (for example an UBSAN splat in VT-d) that is hopefully harmless, but maybe not. Prevent drivers from being called right in iommu_iotlb_sync(). Auditing shows that AMD, Intel, Mediatek and RSIC-V drivers all do things on these empty gathers. Further, there are several callers that can trigger empty gathers, especially in unusual conditions. For example iommu_map_nosync() will call a 0 size unmap on some error paths. Also in VFIO, iommupt and other places. Cc: stable@vger.kernel.org Reported-by: Janusz Krzysztofik Closes: https://lore.kernel.org/r/11145826.aFP6jjVeTY@jkrzyszt-mobl2.ger.corp.intel.com Signed-off-by: Jason Gunthorpe Reviewed-by: Lu Baolu Reviewed-by: Samiullah Khawaja Reviewed-by: Robin Murphy Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 54b8b48c762e88..555597b54083cd 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -980,7 +980,8 @@ static inline void iommu_flush_iotlb_all(struct iommu_domain *domain) static inline void iommu_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *iotlb_gather) { - if (domain->ops->iotlb_sync) + if (domain->ops->iotlb_sync && + likely(iotlb_gather->start < iotlb_gather->end)) domain->ops->iotlb_sync(domain, iotlb_gather); iommu_iotlb_gather_init(iotlb_gather); From ee6e69d032550687a3422504bfca3f834c7b5061 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 2 Mar 2026 18:22:53 -0400 Subject: [PATCH 148/700] iommupt: Fix short gather if the unmap goes into a large mapping unmap has the odd behavior that it can unmap more than requested if the ending point lands within the middle of a large or contiguous IOPTE. In this case the gather should flush everything unmapped which can be larger than what was requested to be unmapped. The gather was only flushing the range requested to be unmapped, not extending to the extra range, resulting in a short invalidation if the caller hits this special condition. This was found by the new invalidation/gather test I am adding in preparation for ARMv8. Claude deduced the root cause. As far as I remember nothing relies on unmapping a large entry, so this is likely not a triggerable bug. Cc: stable@vger.kernel.org Fixes: 7c53f4238aa8 ("iommupt: Add unmap_pages op") Signed-off-by: Jason Gunthorpe Reviewed-by: Lu Baolu Reviewed-by: Samiullah Khawaja Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel --- drivers/iommu/generic_pt/iommu_pt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h index 3e33fe64feab22..7e7a6e7abdeed1 100644 --- a/drivers/iommu/generic_pt/iommu_pt.h +++ b/drivers/iommu/generic_pt/iommu_pt.h @@ -1057,7 +1057,7 @@ size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova, pt_walk_range(&range, __unmap_range, &unmap); - gather_range_pages(iotlb_gather, iommu_table, iova, len, + gather_range_pages(iotlb_gather, iommu_table, iova, unmap.unmapped, &unmap.free_list); return unmap.unmapped; From 8b72aa5704c77380742346d4ac755b074b7f9eaa Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Thu, 26 Mar 2026 09:17:19 -0700 Subject: [PATCH 149/700] iommupt/amdv1: mark amdv1pt_install_leaf_entry as __always_inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After enabling CONFIG_GCOV_KERNEL and CONFIG_GCOV_PROFILE_ALL, following build failure is observed under GCC 14.2.1: In function 'amdv1pt_install_leaf_entry', inlined from '__do_map_single_page' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:650:3, inlined from '__map_single_page0' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:661:1, inlined from 'pt_descend' at drivers/iommu/generic_pt/fmt/../pt_iter.h:391:9, inlined from '__do_map_single_page' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:657:10, inlined from '__map_single_page1.constprop' at drivers/iommu/generic_pt/fmt/../iommu_pt.h:661:1: ././include/linux/compiler_types.h:706:45: error: call to '__compiletime_assert_71' declared with attribute error: FIELD_PREP: value too large for the field 706 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ...... drivers/iommu/generic_pt/fmt/amdv1.h:220:26: note: in expansion of macro 'FIELD_PREP' 220 | FIELD_PREP(AMDV1PT_FMT_OA, | ^~~~~~~~~~ In the path '__do_map_single_page()', level 0 always invokes 'pt_install_leaf_entry(&pts, map->oa, PAGE_SHIFT, …)'. At runtime that lands in the 'if (oasz_lg2 == isz_lg2)' arm of 'amdv1pt_install_leaf_entry()'; the contiguous-only 'else' block is unreachable for 4 KiB pages. With CONFIG_GCOV_KERNEL + CONFIG_GCOV_PROFILE_ALL, the extra instrumentation changes GCC's inlining so that the "dead" 'else' branch still gets instantiated. The compiler constant-folds the contiguous OA expression, runs the 'FIELD_PREP()' compile-time check, and produces: FIELD_PREP: value too large for the field gcov-enabled builds therefore fail even though the code path never executes. Fix this by marking amdv1pt_install_leaf_entry as __always_inline. Fixes: dcd6a011a8d5 ("iommupt: Add map_pages op") Suggested-by: Jason Gunthorpe Signed-off-by: Sherry Yang Signed-off-by: Joerg Roedel --- drivers/iommu/generic_pt/fmt/amdv1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/generic_pt/fmt/amdv1.h b/drivers/iommu/generic_pt/fmt/amdv1.h index 3b2c41d9654d72..8d11b08291d733 100644 --- a/drivers/iommu/generic_pt/fmt/amdv1.h +++ b/drivers/iommu/generic_pt/fmt/amdv1.h @@ -191,7 +191,7 @@ static inline enum pt_entry_type amdv1pt_load_entry_raw(struct pt_state *pts) } #define pt_load_entry_raw amdv1pt_load_entry_raw -static inline void +static __always_inline void amdv1pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa, unsigned int oasz_lg2, const struct pt_write_attrs *attrs) From 7f138de156b20d9f9da6f72f90b63c01941d97d3 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 27 Mar 2026 01:14:12 +0800 Subject: [PATCH 150/700] auxdisplay: line-display: fix NULL dereference in linedisp_release linedisp_release() currently retrieves the enclosing struct linedisp via to_linedisp(). That lookup depends on the attachment list, but the attachment may already have been removed before put_device() invokes the release callback. This can happen in linedisp_unregister(), and can also be reached from some linedisp_register() error paths. In that case, to_linedisp() returns NULL and linedisp_release() dereferences it while freeing the display resources. The struct device released here is the embedded linedisp->dev used by linedisp_register(), so retrieve the enclosing object directly with container_of() instead. Fixes: 66c93809487e ("auxdisplay: linedisp: encapsulate container_of usage within to_linedisp") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Geert Uytterhoeven Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/line-display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c index 81b4aac65807db..fb6d9294140d99 100644 --- a/drivers/auxdisplay/line-display.c +++ b/drivers/auxdisplay/line-display.c @@ -365,7 +365,7 @@ static DEFINE_IDA(linedisp_id); static void linedisp_release(struct device *dev) { - struct linedisp *linedisp = to_linedisp(dev); + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); kfree(linedisp->map); kfree(linedisp->message); From d802d848308b35220f21a8025352f0c0aba15c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Sevens?= Date: Mon, 23 Mar 2026 16:11:07 +0000 Subject: [PATCH 151/700] HID: roccat: fix use-after-free in roccat_report_event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit roccat_report_event() iterates over the device->readers list without holding the readers_lock. This allows a concurrent roccat_release() to remove and free a reader while it's still being accessed, leading to a use-after-free. Protect the readers list traversal with the readers_lock mutex. Signed-off-by: Benoît Sevens Reviewed-by: Silvan Jegen Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index c7f7562e22e562..e413662f750824 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -257,6 +257,7 @@ int roccat_report_event(int minor, u8 const *data) if (!new_value) return -ENOMEM; + mutex_lock(&device->readers_lock); mutex_lock(&device->cbuf_lock); report = &device->cbuf[device->cbuf_end]; @@ -279,6 +280,7 @@ int roccat_report_event(int minor, u8 const *data) } mutex_unlock(&device->cbuf_lock); + mutex_unlock(&device->readers_lock); wake_up_interruptible(&device->wait); return 0; From 532743944324a873bbaf8620fcabcd0e69e30c36 Mon Sep 17 00:00:00 2001 From: leo vriska Date: Wed, 4 Mar 2026 13:36:59 -0500 Subject: [PATCH 152/700] HID: quirks: add HID_QUIRK_ALWAYS_POLL for 8BitDo Pro 3 According to a mailing list report [1], this controller's predecessor has the same issue. However, it uses the xpad driver instead of HID, so this quirk wouldn't apply. [1]: https://lore.kernel.org/linux-input/unufo3$det$1@ciao.gmane.io/ Signed-off-by: leo vriska Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-quirks.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index afcee13bad6139..e432cc0df680b9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -22,6 +22,9 @@ #define USB_DEVICE_ID_3M2256 0x0502 #define USB_DEVICE_ID_3M3266 0x0506 +#define USB_VENDOR_ID_8BITDO 0x2dc8 +#define USB_DEVICE_ID_8BITDO_PRO_3 0x6009 + #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 #define USB_DEVICE_ID_A4TECH_X5_005D 0x000a diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 3217e436c052c9..f6be3ffee02326 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -25,6 +25,7 @@ */ static const struct hid_device_id hid_quirks[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_8BITDO, USB_DEVICE_ID_8BITDO_PRO_3), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD), HID_QUIRK_BADPAD }, { HID_USB_DEVICE(USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR), HID_QUIRK_BADPAD }, { HID_USB_DEVICE(USB_VENDOR_ID_ADATA_XPG, USB_VENDOR_ID_ADATA_XPG_WL_GAMING_MOUSE), HID_QUIRK_ALWAYS_POLL }, From e1415b9418eb22b4a7a1ef4b4aec9dd0a49e3fa7 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 25 Mar 2026 12:26:38 -0700 Subject: [PATCH 153/700] platform/x86: ISST: Reset core count to 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on feature revision, number of buckets can be less than the TRL_MAX_BUCKETS. In that case core counts in the remaining buckets can be set to some invalid values. Hence reset core count to 0 for all buckets before assigning correct values. Fixes: 885d1c2a30b7 ("platform/x86: ISST: Support SST-TF revision 2") Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325192638.3417281-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index e238c3105c78e7..63f08d66703acc 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1461,6 +1461,8 @@ static int isst_if_get_turbo_freq_info(void __user *argp) SST_MUL_FACTOR_FREQ) } + memset(turbo_freq.bucket_core_counts, 0, sizeof(turbo_freq.bucket_core_counts)); + if (feature_rev >= 2) { bool has_tf_info_8 = false; From 4ab604b3f3aa8dcccc7505f5d310016682a99d5f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 25 Mar 2026 12:29:09 -0700 Subject: [PATCH 154/700] platform/x86/intel-uncore-freq: Handle autonomous UFS status bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the AUTONOMOUS_UFS_DISABLED bit is set in the header, the ELC (Efficiency Latency Control) feature is non-functional. Hence, return error for read or write to ELC attributes. Fixes: bb516dc79c4a ("platform/x86/intel-uncore-freq: Add support for efficiency latency control") Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325192909.3417322-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-tpmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 1237d957088650..4c7e64db478c02 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -537,6 +537,7 @@ static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_ #define UNCORE_VERSION_MASK GENMASK_ULL(7, 0) #define UNCORE_LOCAL_FABRIC_CLUSTER_ID_MASK GENMASK_ULL(15, 8) #define UNCORE_CLUSTER_OFF_MASK GENMASK_ULL(7, 0) +#define UNCORE_AUTONOMOUS_UFS_DISABLED BIT(32) #define UNCORE_MAX_CLUSTER_PER_DOMAIN 8 static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) @@ -598,6 +599,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ for (i = 0; i < num_resources; ++i) { struct tpmi_uncore_power_domain_info *pd_info; + bool auto_ufs_enabled; struct resource *res; u64 cluster_offset; u8 cluster_mask; @@ -647,6 +649,8 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ continue; } + auto_ufs_enabled = !(header & UNCORE_AUTONOMOUS_UFS_DISABLED); + /* Find out number of clusters in this resource */ pd_info->cluster_count = hweight8(cluster_mask); @@ -689,7 +693,9 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ cluster_info->uncore_root = tpmi_uncore; - if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) >= UNCORE_ELC_SUPPORTED_VERSION) + if ((TPMI_MINOR_VERSION(pd_info->ufs_header_ver) >= + UNCORE_ELC_SUPPORTED_VERSION) && + auto_ufs_enabled) cluster_info->elc_supported = true; ret = uncore_freq_add_entry(&cluster_info->uncore_data, 0); From ba7d46e0a384398c58eee34579986f830260d37b Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 25 Mar 2026 12:31:57 -0700 Subject: [PATCH 155/700] platform/x86: ISST: Increase minor version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation doesn't require any changes to support minor version 3, hence increment it to avoid "Unsupported minor version:3" message. Signed-off-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260325193158.3417382-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 63f08d66703acc..b804cb753f9428 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -36,7 +36,7 @@ /* Supported SST hardware version by this driver */ #define ISST_MAJOR_VERSION 0 -#define ISST_MINOR_VERSION 2 +#define ISST_MINOR_VERSION 3 /* * Used to indicate if value read from MMIO needs to get multiplied From b7f4e7babc122426e8802dffdd8ff1501db6adf4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 25 Mar 2026 12:31:58 -0700 Subject: [PATCH 156/700] platform/x86/intel-uncore-freq: Increase minor version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation doesn't require any changes to support minor version 3, hence increment it to avoid "Unsupported minor version:3" message. Signed-off-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260325193158.3417382-2-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 4c7e64db478c02..88015a2c6a0d12 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -31,7 +31,7 @@ #include "uncore-frequency-common.h" #define UNCORE_MAJOR_VERSION 0 -#define UNCORE_MINOR_VERSION 2 +#define UNCORE_MINOR_VERSION 3 #define UNCORE_ELC_SUPPORTED_VERSION 2 #define UNCORE_HEADER_INDEX 0 #define UNCORE_FABRIC_CLUSTER_OFFSET 8 From 1a9452c428a6b76f0b797bae21daa454fccef1a2 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 24 Mar 2026 16:16:41 -0500 Subject: [PATCH 157/700] platform/x86/amd: pmc: Add Thinkpad L14 Gen3 to quirk_s2idle_bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This platform is a similar vintage of platforms that had a BIOS bug leading to a 10s delay at resume from s0i3. Add a quirk for it. Reported-by: Imrane Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221273 Tested-by: Imrane Signed-off-by: Mario Limonciello Link: https://patch.msgid.link/20260324211647.357924-1-mario.limonciello@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index ed285afaf9b0d7..24506e3429430f 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -203,6 +203,15 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */ + { + .ident = "Thinkpad L14 Gen3", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21C6"), + } + }, /* https://gitlab.freedesktop.org/drm/amd/-/issues/4434 */ { .ident = "Lenovo Yoga 6 13ALC6", From 5e67ba9bb531e1ec6599a82a065dea9040b9ce50 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 25 Mar 2026 15:41:52 +0800 Subject: [PATCH 158/700] net/ipv6: ioam6: prevent schema length wraparound in trace fill ioam6_fill_trace_data() stores the schema contribution to the trace length in a u8. With bit 22 enabled and the largest schema payload, sclen becomes 1 + 1020 / 4, wraps from 256 to 0, and bypasses the remaining-space check. __ioam6_fill_trace_data() then positions the write cursor without reserving the schema area but still copies the 4-byte schema header and the full schema payload, overrunning the trace buffer. Keep sclen in an unsigned int so the remaining-space check and the write cursor calculation both see the full schema length. Fixes: 8c6f6fa67726 ("ipv6: ioam: IOAM Generic Netlink API") Signed-off-by: Pengpeng Hou Reviewed-by: Justin Iurman Signed-off-by: David S. Miller --- net/ipv6/ioam6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index b76f89d92e7b2c..3978773bec4248 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -708,7 +708,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, struct ioam6_namespace *ns, struct ioam6_trace_hdr *trace, struct ioam6_schema *sc, - u8 sclen, bool is_input) + unsigned int sclen, bool is_input) { struct net_device *dev = skb_dst_dev(skb); struct timespec64 ts; @@ -939,7 +939,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb, bool is_input) { struct ioam6_schema *sc; - u8 sclen = 0; + unsigned int sclen = 0; /* Skip if Overflow flag is set */ From bb417456c7814d1493d98b7dd9c040bf3ce3b4ed Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Wed, 25 Mar 2026 12:20:53 +0100 Subject: [PATCH 159/700] tg3: Fix race for querying speed/duplex When driver signals carrier up via netif_carrier_on() its internal link_up state isn't updated immediately. This leads to inconsistent speed/duplex in /proc/net/bonding/bondX where the speed and duplex is shown as unknown while ethtool shows correct values. Fix this by using netif_carrier_ok() for link checking in get_ksettings function. Fixes: 84421b99cedc ("tg3: Update link_up flag for phylib devices") Signed-off-by: Thomas Bogendoerfer Reviewed-by: Pavan Chebbi Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 21a5dd34272442..73a4b569b03e31 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -12299,7 +12299,7 @@ static int tg3_get_link_ksettings(struct net_device *dev, ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, advertising); - if (netif_running(dev) && tp->link_up) { + if (netif_running(dev) && netif_carrier_ok(dev)) { cmd->base.speed = tp->link_config.active_speed; cmd->base.duplex = tp->link_config.active_duplex; ethtool_convert_legacy_u32_to_link_mode( From 5597dd284ff8c556c0b00f6a34473677426e3f81 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 25 Mar 2026 12:51:30 +0000 Subject: [PATCH 160/700] net: ti: icssg-prueth: fix missing data copy and wrong recycle in ZC RX dispatch emac_dispatch_skb_zc() allocates a new skb via napi_alloc_skb() but never copies the packet data from the XDP buffer into it. The skb is passed up the stack containing uninitialized heap memory instead of the actual received packet, leaking kernel heap contents to userspace. Copy the received packet data from the XDP buffer into the skb using skb_copy_to_linear_data(). Additionally, remove the skb_mark_for_recycle() call since the skb is backed by the NAPI page frag allocator, not page_pool. Marking a non-page_pool skb for recycle causes the free path to return pages to a page_pool that does not own them, corrupting page_pool state. The non-ZC path (emac_rx_packet) does not have these issues because it uses napi_build_skb() to wrap the existing page_pool page directly, requiring no copy, and correctly marks for recycle since the page comes from page_pool_dev_alloc_pages(). Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX") Signed-off-by: David Carlier Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/icssg/icssg_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index fd4e7622f1231b..a28a608f9bf4b9 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -902,6 +902,7 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, skb_reserve(skb, headroom); skb_put(skb, pkt_len); + skb_copy_to_linear_data(skb, xdp->data, pkt_len); skb->dev = ndev; /* RX HW timestamp */ @@ -912,7 +913,6 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, skb->offload_fwd_mark = emac->offload_fwd_mark; skb->protocol = eth_type_trans(skb, ndev); - skb_mark_for_recycle(skb); napi_gro_receive(&emac->napi_rx, skb); ndev->stats.rx_bytes += pkt_len; ndev->stats.rx_packets++; From 743677a8cb30b09f16a7f167f497c2c927891b5a Mon Sep 17 00:00:00 2001 From: Maximilian Pezzullo Date: Wed, 4 Mar 2026 09:25:22 +0100 Subject: [PATCH 161/700] HID: amd_sfh: don't log error when device discovery fails with -EOPNOTSUPP When sensor discovery fails on systems without AMD SFH sensors, the code already emits a warning via dev_warn() in amd_sfh_hid_client_init(). The subsequent dev_err() in sfh_init_work() for the same -EOPNOTSUPP return value is redundant and causes unnecessary alarm. Suppress the dev_err() for -EOPNOTSUPP to avoid confusing users who have no AMD SFH sensors. Fixes: 2105e8e00da4 ("HID: amd_sfh: Improve boot time when SFH is available") Reported-by: Casey Croy Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221099 Signed-off-by: Maximilian Pezzullo Acked-by: Basavaraj Natikar Signed-off-by: Jiri Kosina --- drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 1d9f955573aa43..4b81cebdc33590 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -413,7 +413,8 @@ static void sfh_init_work(struct work_struct *work) rc = amd_sfh_hid_client_init(mp2); if (rc) { amd_sfh_clear_intr(mp2); - dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc); + if (rc != -EOPNOTSUPP) + dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc); return; } From 1a2d30aa73140e008e62dc5898e4c2c3b59b35e4 Mon Sep 17 00:00:00 2001 From: Lode Willems Date: Tue, 24 Mar 2026 21:43:06 +0100 Subject: [PATCH 162/700] HID: Kysona: Add support for VXE Dragonfly R1 Pro Apparently this same protocol is used by more mice from different brands. This patch adds support for the VXE Dragonfly R1 Pro. Tested-by: Dominykas Svetikas Signed-off-by: Lode Willems Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 4 ++++ drivers/hid/hid-kysona.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e432cc0df680b9..c1e4a6ce96317a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1474,6 +1474,10 @@ #define USB_VENDOR_ID_VTL 0x0306 #define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f +#define USB_VENDOR_ID_VXE 0x3554 +#define USB_DEVICE_ID_VXE_DRAGONFLY_R1_PRO_DONGLE 0xf58a +#define USB_DEVICE_ID_VXE_DRAGONFLY_R1_PRO_WIRED 0xf58c + #define USB_VENDOR_ID_WACOM 0x056a #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 #define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD diff --git a/drivers/hid/hid-kysona.c b/drivers/hid/hid-kysona.c index 09bfe30d02cb90..ccbd8380064ea5 100644 --- a/drivers/hid/hid-kysona.c +++ b/drivers/hid/hid-kysona.c @@ -272,6 +272,8 @@ static void kysona_remove(struct hid_device *hdev) static const struct hid_device_id kysona_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYSONA, USB_DEVICE_ID_KYSONA_M600_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYSONA, USB_DEVICE_ID_KYSONA_M600_WIRED) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VXE, USB_DEVICE_ID_VXE_DRAGONFLY_R1_PRO_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VXE, USB_DEVICE_ID_VXE_DRAGONFLY_R1_PRO_WIRED) }, { } }; MODULE_DEVICE_TABLE(hid, kysona_devices); From fffca572f9ca51607f180a37d0c898404c8f9112 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 15:06:31 +0100 Subject: [PATCH 163/700] mpage: Provide variant of mpage_writepages() with own optional folio handler Some filesystems need to treat some folios specially (for example for inodes with inline data). Doing the handling in their .writepages method in a race-free manner results in duplicating some of the writeback internals. So provide generalized version of mpage_writepages() that allows filesystem to provide a handler called for each folio which can handle the folio in a special way. Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260326140635.15895-3-jack@suse.cz Signed-off-by: Jan Kara --- fs/mpage.c | 30 ++++++++++++++++++++++++------ include/linux/mpage.h | 11 +++++++++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/fs/mpage.c b/fs/mpage.c index 7dae5afc2b9ee2..b3d9f231a04aa8 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -646,17 +646,24 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio, } /** - * mpage_writepages - walk the list of dirty pages of the given address space & writepage() all of them + * __mpage_writepages - walk the list of dirty pages of the given address space + * & writepage() all of them * @mapping: address space structure to write * @wbc: subtract the number of written pages from *@wbc->nr_to_write * @get_block: the filesystem's block mapper function. + * @write_folio: handler to call for each folio before calling + * mpage_write_folio() * * This is a library function, which implements the writepages() - * address_space_operation. + * address_space_operation. It calls @write_folio handler for each folio. If + * the handler returns value > 0, it calls mpage_write_folio() to do the + * folio writeback. */ int -mpage_writepages(struct address_space *mapping, - struct writeback_control *wbc, get_block_t get_block) +__mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block, + int (*write_folio)(struct folio *folio, + struct writeback_control *wbc)) { struct mpage_data mpd = { .get_block = get_block, @@ -666,11 +673,22 @@ mpage_writepages(struct address_space *mapping, int error; blk_start_plug(&plug); - while ((folio = writeback_iter(mapping, wbc, folio, &error))) + while ((folio = writeback_iter(mapping, wbc, folio, &error))) { + if (write_folio) { + error = write_folio(folio, wbc); + /* + * == 0 means folio is handled, < 0 means error. In + * both cases hand back control to writeback_iter() + */ + if (error <= 0) + continue; + /* Let mpage_write_folio() handle the folio. */ + } error = mpage_write_folio(wbc, folio, &mpd); + } if (mpd.bio) mpage_bio_submit_write(mpd.bio); blk_finish_plug(&plug); return error; } -EXPORT_SYMBOL(mpage_writepages); +EXPORT_SYMBOL(__mpage_writepages); diff --git a/include/linux/mpage.h b/include/linux/mpage.h index 1bdc39daac0a3e..358946990bfa55 100644 --- a/include/linux/mpage.h +++ b/include/linux/mpage.h @@ -17,7 +17,14 @@ struct readahead_control; void mpage_readahead(struct readahead_control *, get_block_t get_block); int mpage_read_folio(struct folio *folio, get_block_t get_block); -int mpage_writepages(struct address_space *mapping, - struct writeback_control *wbc, get_block_t get_block); +int __mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block, + int (*write_folio)(struct folio *folio, + struct writeback_control *wbc)); +static inline int mpage_writepages(struct address_space *mapping, + struct writeback_control *wbc, get_block_t get_block) +{ + return __mpage_writepages(mapping, wbc, get_block, NULL); +} #endif From 102e57d56f81fa5c5ed78f576101d1bf1b3e6fe2 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 15:06:32 +0100 Subject: [PATCH 164/700] udf: Fix race between file type conversion and writeback udf_setsize() can race with udf_writepages() as follows: udf_setsize() udf_writepages() if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) err = udf_expand_file_adinicb(inode); err = udf_extend_file(inode, newsize); udf_adinicb_writepages() memcpy_from_file_folio() - crash because inode size is too big. Fix the problem by checking the file type under folio lock in udf_handle_page_wb() handler called from __mpage_writepages() which properly serializes with udf_expand_file_adinicb(). Reported-by: Jianzhou Zhao Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260326140635.15895-4-jack@suse.cz Signed-off-by: Jan Kara --- fs/udf/inode.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 7fae8002344a0e..23e894092dab57 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -181,22 +181,23 @@ static void udf_write_failed(struct address_space *mapping, loff_t to) } } -static int udf_adinicb_writepages(struct address_space *mapping, - struct writeback_control *wbc) +static int udf_handle_page_wb(struct folio *folio, + struct writeback_control *wbc) { - struct inode *inode = mapping->host; + struct inode *inode = folio->mapping->host; struct udf_inode_info *iinfo = UDF_I(inode); - struct folio *folio = NULL; - int error = 0; - while ((folio = writeback_iter(mapping, wbc, folio, &error))) { - BUG_ON(!folio_test_locked(folio)); - BUG_ON(folio->index != 0); - memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio, - 0, i_size_read(inode)); - folio_unlock(folio); - } + /* + * Inodes in the normal format are handled by the generic code. This + * check is race-free as the folio lock protects us from inode type + * conversion. + */ + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) + return 1; + memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio, + 0, i_size_read(inode)); + folio_unlock(folio); mark_inode_dirty(inode); return 0; } @@ -204,12 +205,8 @@ static int udf_adinicb_writepages(struct address_space *mapping, static int udf_writepages(struct address_space *mapping, struct writeback_control *wbc) { - struct inode *inode = mapping->host; - struct udf_inode_info *iinfo = UDF_I(inode); - - if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) - return udf_adinicb_writepages(mapping, wbc); - return mpage_writepages(mapping, wbc, udf_get_block_wb); + return __mpage_writepages(mapping, wbc, udf_get_block_wb, + udf_handle_page_wb); } static void udf_adinicb_read_folio(struct folio *folio) From 2feec5ae5df785658924ab6bd91280dc3926507c Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Thu, 5 Feb 2026 13:34:14 +0100 Subject: [PATCH 165/700] accel/qaic: Handle DBC deactivation if the owner went away When a DBC is released, the device sends a QAIC_TRANS_DEACTIVATE_FROM_DEV transaction to the host over the QAIC_CONTROL MHI channel. QAIC handles this by calling decode_deactivate() to release the resources allocated for that DBC. Since that handling is done in the qaic_manage_ioctl() context, if the user goes away before receiving and handling the deactivation, the host will be out-of-sync with the DBCs available for use, and the DBC resources will not be freed unless the device is removed. If another user loads and requests to activate a network, then the device assigns the same DBC to that network, QAIC will "indefinitely" wait for dbc->in_use = false, leading the user process to hang. As a solution to this, handle QAIC_TRANS_DEACTIVATE_FROM_DEV transactions that are received after the user has gone away. Fixes: 129776ac2e38 ("accel/qaic: Add control path") Signed-off-by: Youssef Samir Reviewed-by: Lizhi Hou Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260205123415.3870898-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 47 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index f698d5dfd3264f..43f84d43896029 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -914,7 +914,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len */ return -ENODEV; - if (status) { + if (usr && status) { /* * Releasing resources failed on the device side, which puts * us in a bind since they may still be in use, so enable the @@ -1109,6 +1109,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u mutex_lock(&qdev->cntl_mutex); if (!list_empty(&elem.list)) list_del(&elem.list); + /* resp_worker() processed the response but the wait was interrupted */ + else if (ret == -ERESTARTSYS) + ret = 0; if (!ret && !elem.buf) ret = -ETIMEDOUT; else if (ret > 0 && !elem.buf) @@ -1419,9 +1422,49 @@ static void resp_worker(struct work_struct *work) } mutex_unlock(&qdev->cntl_mutex); - if (!found) + if (!found) { + /* + * The user might have gone away at this point without waiting + * for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from + * the device. If this is not handled correctly, the host will + * not know that the DBC[n] has been freed on the device. + * Due to this failure in synchronization between the device and + * the host, if another user requests to activate a network, and + * the device assigns DBC[n] again, save_dbc_buf() will hang, + * waiting for dbc[n]->in_use to be set to false, which will not + * happen unless the qaic_dev_reset_clean_local_state() gets + * called by resetting the device (or re-inserting the module). + * + * As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV + * transactions in the message before disposing of it, then + * handle releasing the DBC resources. + * + * Since the user has gone away, if the device could not + * deactivate the network (status != 0), there is no way to + * enable and reassign the DBC to the user. We can put trust in + * the device that it will release all the active DBCs in + * response to the QAIC_TRANS_TERMINATE_TO_DEV transaction, + * otherwise, the user can issue an soc_reset to the device. + */ + u32 msg_count = le32_to_cpu(msg->hdr.count); + u32 msg_len = le32_to_cpu(msg->hdr.len); + u32 len = 0; + int j; + + for (j = 0; j < msg_count && len < msg_len; ++j) { + struct wire_trans_hdr *trans_hdr; + + trans_hdr = (struct wire_trans_hdr *)(msg->data + len); + if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) { + if (decode_deactivate(qdev, trans_hdr, &len, NULL)) + len += le32_to_cpu(trans_hdr->len); + } else { + len += le32_to_cpu(trans_hdr->len); + } + } /* request must have timed out, drop packet */ kfree(msg); + } kfree(resp); } From e5046823f8fa3677341b541a25af2fcb99a5b1e0 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 25 Mar 2026 20:29:20 -0700 Subject: [PATCH 166/700] lib/crypto: chacha: Zeroize permuted_state before it leaves scope Since the ChaCha permutation is invertible, the local variable 'permuted_state' is sufficient to compute the original 'state', and thus the key, even after the permutation has been done. While the kernel is quite inconsistent about zeroizing secrets on the stack (and some prominent userspace crypto libraries don't bother at all since it's not guaranteed to work anyway), the kernel does try to do it as a best practice, especially in cases involving the RNG. Thus, explicitly zeroize 'permuted_state' before it goes out of scope. Fixes: c08d0e647305 ("crypto: chacha20 - Add a generic ChaCha20 stream cipher implementation") Cc: stable@vger.kernel.org Acked-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20260326032920.39408-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/chacha-block-generic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/crypto/chacha-block-generic.c b/lib/crypto/chacha-block-generic.c index 77f68de71066a9..4a6d627580cb62 100644 --- a/lib/crypto/chacha-block-generic.c +++ b/lib/crypto/chacha-block-generic.c @@ -87,6 +87,8 @@ void chacha_block_generic(struct chacha_state *state, &out[i * sizeof(u32)]); state->x[12]++; + + chacha_zeroize_state(&permuted_state); } EXPORT_SYMBOL(chacha_block_generic); @@ -110,5 +112,7 @@ void hchacha_block_generic(const struct chacha_state *state, memcpy(&out[0], &permuted_state.x[0], 16); memcpy(&out[4], &permuted_state.x[12], 16); + + chacha_zeroize_state(&permuted_state); } EXPORT_SYMBOL(hchacha_block_generic); From 86ab3e55673a7a49a841838776f1ab18d23a67b5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Mar 2026 20:26:08 +0000 Subject: [PATCH 167/700] ipv6: icmp: clear skb2->cb[] in ip6_err_gen_icmpv6_unreach() Sashiko AI-review observed: In ip6_err_gen_icmpv6_unreach(), the skb is an outer IPv4 ICMP error packet where its cb contains an IPv4 inet_skb_parm. When skb is cloned into skb2 and passed to icmp6_send(), it uses IP6CB(skb2). IP6CB interprets the IPv4 inet_skb_parm as an inet6_skb_parm. The cipso offset in inet_skb_parm.opt directly overlaps with dsthao in inet6_skb_parm at offset 18. If an attacker sends a forged ICMPv4 error with a CIPSO IP option, dsthao would be a non-zero offset. Inside icmp6_send(), mip6_addr_swap() is called and uses ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO). This would scan the inner, attacker-controlled IPv6 packet starting at that offset, potentially returning a fake TLV without checking if the remaining packet length can hold the full 18-byte struct ipv6_destopt_hao. Could mip6_addr_swap() then perform a 16-byte swap that extends past the end of the packet data into skb_shared_info? Should the cb array also be cleared in ip6_err_gen_icmpv6_unreach() and ip6ip6_err() to prevent this? This patch implements the first suggestion. I am not sure if ip6ip6_err() needs to be changed. A separate patch would be better anyway. Fixes: ca15a078bd90 ("sit: generate icmpv6 error when receiving icmpv4 error") Reported-by: Ido Schimmel Closes: https://sashiko.dev/#/patchset/20260326155138.2429480-1-edumazet%40google.com Signed-off-by: Eric Dumazet Cc: Oskar Kjos Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260326202608.2976021-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/icmp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 813d2e9edb8bed..d5d23a9296eac8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -875,6 +875,9 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type, if (!skb2) return 1; + /* Remove debris left by IPv4 stack. */ + memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, nhs); skb_reset_network_header(skb2); From 2edfa31769a4add828a7e604b21cb82aaaa05925 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Mar 2026 15:51:38 +0000 Subject: [PATCH 168/700] ip6_tunnel: clear skb2->cb[] in ip4ip6_err() Oskar Kjos reported the following problem. ip4ip6_err() calls icmp_send() on a cloned skb whose cb[] was written by the IPv6 receive path as struct inet6_skb_parm. icmp_send() passes IPCB(skb2) to __ip_options_echo(), which interprets that cb[] region as struct inet_skb_parm (IPv4). The layouts differ: inet6_skb_parm.nhoff at offset 14 overlaps inet_skb_parm.opt.rr, producing a non-zero rr value. __ip_options_echo() then reads optlen from attacker-controlled packet data at sptr[rr+1] and copies that many bytes into dopt->__data, a fixed 40-byte stack buffer (IP_OPTIONS_DATA_FIXED_SIZE). To fix this we clear skb2->cb[], as suggested by Oskar Kjos. Also add minimal IPv4 header validation (version == 4, ihl >= 5). Fixes: c4d3efafcc93 ("[IPV6] IP6TUNNEL: Add support to IPv4 over IPv6 tunnel.") Reported-by: Oskar Kjos Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260326155138.2429480-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_tunnel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4c29aa94e86eb9..0b53488a922900 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -601,11 +601,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!skb2) return 0; + /* Remove debris left by IPv6 stack. */ + memset(IPCB(skb2), 0, sizeof(*IPCB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); eiph = ip_hdr(skb2); + if (eiph->version != 4 || eiph->ihl < 5) + goto out; /* Try to guess incoming interface */ rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr, From b38c55320bf85a84a4f04803c57b261fc87e9b4b Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Tue, 24 Mar 2026 12:51:22 -0700 Subject: [PATCH 169/700] eth: fbnic: Account for page fragments when updating BDQ tail FBNIC supports fixed size buffers of 4K. When PAGE_SIZE > 4K, we fragment the page across multiple descriptors (FBNIC_BD_FRAG_COUNT). When refilling the BDQ, the correct number of entries are populated, but tail was only incremented by one. So on a system with 64K pages, HW would get one descriptor refilled for every 16 we populate. Additionally, we program the ring size in the HW when enabling the BDQ. This was not accounting for page fragments, so on systems with 64K pages, the HW used 1/16th of the ring. Fixes: 0cb4c0a13723 ("eth: fbnic: Implement Rx queue alloc/start/stop/free") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260324195123.3486219-2-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 9fb91d4f397196..9cd85a0d0c3a7f 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -927,7 +927,7 @@ static void fbnic_fill_bdq(struct fbnic_ring *bdq) /* Force DMA writes to flush before writing to tail */ dma_wmb(); - writel(i, bdq->doorbell); + writel(i * FBNIC_BD_FRAG_COUNT, bdq->doorbell); } } @@ -2564,7 +2564,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) hpq->tail = 0; hpq->head = 0; - log_size = fls(hpq->size_mask); + log_size = fls(hpq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Store descriptor ring address and size */ fbnic_ring_wr32(hpq, FBNIC_QUEUE_BDQ_HPQ_BAL, lower_32_bits(hpq->dma)); @@ -2576,7 +2576,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) if (!ppq->size_mask) goto write_ctl; - log_size = fls(ppq->size_mask); + log_size = fls(ppq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Add enabling of PPQ to BDQ control */ bdq_ctl |= FBNIC_QUEUE_BDQ_CTL_PPQ_ENABLE; From f3567dd428b264b3f06f881e5e85a738c7c910df Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Tue, 24 Mar 2026 12:51:23 -0700 Subject: [PATCH 170/700] eth: fbnic: Fix debugfs output for BDQ's with page frags The rings size_mask represents the number of pages, so we need to determine the number of page frags when dumping the descriptors. Fixes: df04373b0dab ("eth fbnic: Add debugfs hooks for tx/rx rings") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260324195123.3486219-3-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c index 08270db2dee864..3c4563c8f403fa 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c @@ -197,7 +197,7 @@ static int fbnic_dbg_bdq_desc_seq_show(struct seq_file *s, void *v) return 0; } - for (i = 0; i <= ring->size_mask; i++) { + for (i = 0; i < (ring->size_mask + 1) * FBNIC_BD_FRAG_COUNT; i++) { u64 bd = le64_to_cpu(ring->desc[i]); seq_printf(s, "%04x %#04llx %#014llx\n", i, From a01aee7cafc575bb82f5529e8734e7052f9b16ea Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:39 +0000 Subject: [PATCH 171/700] bridge: br_nd_send: linearize skb before parsing ND options br_nd_send() parses neighbour discovery options from ns->opt[] and assumes that these options are in the linear part of request. Its callers only guarantee that the ICMPv6 header and target address are available, so the option area can still be non-linear. Parsing ns->opt[] in that case can access data past the linear buffer. Linearize request before option parsing and derive ns from the linear network header. Fixes: ed842faeb2bd ("bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports") Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-2-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/bridge/br_arp_nd_proxy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 1e2b51769eec81..af3d1e33f50b83 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -251,12 +251,12 @@ struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg) static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, struct sk_buff *request, struct neighbour *n, - __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns) + __be16 vlan_proto, u16 vlan_tci) { struct net_device *dev = request->dev; struct net_bridge_vlan_group *vg; + struct nd_msg *na, *ns; struct sk_buff *reply; - struct nd_msg *na; struct ipv6hdr *pip6; int na_olen = 8; /* opt hdr + ETH_ALEN for target */ int ns_olen; @@ -264,7 +264,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, u8 *daddr; u16 pvid; - if (!dev) + if (!dev || skb_linearize(request)) return; len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + @@ -281,6 +281,8 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, skb_set_mac_header(reply, 0); daddr = eth_hdr(request)->h_source; + ns = (struct nd_msg *)(skb_network_header(request) + + sizeof(struct ipv6hdr)); /* Do we need option processing ? */ ns_olen = request->len - (skb_network_offset(request) + @@ -472,9 +474,9 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, if (vid != 0) br_nd_send(br, p, skb, n, skb->vlan_proto, - skb_vlan_tag_get(skb), msg); + skb_vlan_tag_get(skb)); else - br_nd_send(br, p, skb, n, 0, 0, msg); + br_nd_send(br, p, skb, n, 0, 0); replied = true; } From 850837965af15707fd3142c1cf3c5bfaf022299b Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:40 +0000 Subject: [PATCH 172/700] bridge: br_nd_send: validate ND option lengths br_nd_send() walks ND options according to option-provided lengths. A malformed option can make the parser advance beyond the computed option span or use a too-short source LLADDR option payload. Validate option lengths against the remaining NS option area before advancing, and only read source LLADDR when the option is large enough for an Ethernet address. Fixes: ed842faeb2bd ("bridge: suppress nd pkts on BR_NEIGH_SUPPRESS ports") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-3-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/bridge/br_arp_nd_proxy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index af3d1e33f50b83..6b5595868a39c0 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -288,12 +288,14 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, ns_olen = request->len - (skb_network_offset(request) + sizeof(struct ipv6hdr)) - sizeof(*ns); for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) { - if (!ns->opt[i + 1]) { + if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) { kfree_skb(reply); return; } if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { - daddr = ns->opt + i + sizeof(struct nd_opt_hdr); + if ((ns->opt[i + 1] << 3) >= + sizeof(struct nd_opt_hdr) + ETH_ALEN) + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); break; } } From afa9a05e6c4971bd5586f1b304e14d61fb3d9385 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 26 Mar 2026 03:44:41 +0000 Subject: [PATCH 173/700] vxlan: validate ND option lengths in vxlan_na_create vxlan_na_create() walks ND options according to option-provided lengths. A malformed option can make the parser advance beyond the computed option span or use a too-short source LLADDR option payload. Validate option lengths against the remaining NS option area before advancing, and only read source LLADDR when the option is large enough for an Ethernet address. Fixes: 4b29dba9c085 ("vxlan: fix nonfunctional neigh_reduce()") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Tested-by: Ao Zhou Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Yang Yang Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260326034441.2037420-4-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 17c941aac32db9..a94ac82a613649 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1965,12 +1965,14 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, ns_olen = request->len - skb_network_offset(request) - sizeof(struct ipv6hdr) - sizeof(*ns); for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) { - if (!ns->opt[i + 1]) { + if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) { kfree_skb(reply); return NULL; } if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { - daddr = ns->opt + i + sizeof(struct nd_opt_hdr); + if ((ns->opt[i + 1] << 3) >= + sizeof(struct nd_opt_hdr) + ETH_ALEN) + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); break; } } From 4576100b8cd03118267513cafacde164b498b322 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 13:43:09 -0700 Subject: [PATCH 174/700] net/sched: sch_hfsc: fix divide-by-zero in rtsc_min() m2sm() converts a u32 slope to a u64 scaled value. For large inputs (e.g. m1=4000000000), the result can reach 2^32. rtsc_min() stores the difference of two such u64 values in a u32 variable `dsm` and uses it as a divisor. When the difference is exactly 2^32 the truncation yields zero, causing a divide-by-zero oops in the concave-curve intersection path: Oops: divide error: 0000 RIP: 0010:rtsc_min (net/sched/sch_hfsc.c:601) Call Trace: init_ed (net/sched/sch_hfsc.c:629) hfsc_enqueue (net/sched/sch_hfsc.c:1569) [...] Widen `dsm` to u64 and replace do_div() with div64_u64() so the full difference is preserved. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260326204310.1549327-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/sched/sch_hfsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index b5657ffbbf844b..83b2ca2e37fc82 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -555,7 +555,7 @@ static void rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y) { u64 y1, y2, dx, dy; - u32 dsm; + u64 dsm; if (isc->sm1 <= isc->sm2) { /* service curve is convex */ @@ -598,7 +598,7 @@ rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y) */ dx = (y1 - y) << SM_SHIFT; dsm = isc->sm1 - isc->sm2; - do_div(dx, dsm); + dx = div64_u64(dx, dsm); /* * check if (x, y1) belongs to the 1st segment of rtsc. * if so, add the offset. From 5d17af9eb2dd3de6846ed344c883de7812e6cc09 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 13:43:10 -0700 Subject: [PATCH 175/700] selftests/tc-testing: add test for HFSC divide-by-zero in rtsc_min() Add a regression test for the divide-by-zero in rtsc_min() triggered when m2sm() converts a large m1 value (e.g. 32gbit) to a u64 scaled slope reaching 2^32. rtsc_min() stores the difference of two such u64 values (sm1 - sm2) in a u32 variable `dsm`, truncating 2^32 to zero and causing a divide-by-zero oops in the concave-curve intersection path. The test configures an HFSC class with m1=32gbit d=1ms m2=0bit, sends a packet to activate the class, waits for it to drain and go idle, then sends another packet to trigger reactivation through rtsc_min(). Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260326204310.1549327-2-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/infra/qdiscs.json | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index 6a39640aa2a862..1e5efb2a31eb0b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -1111,5 +1111,30 @@ "teardown": [ "$TC qdisc del dev $DUMMY root handle 1:" ] + }, + { + "id": "a3d7", + "name": "HFSC with large m1 - no divide-by-zero on class reactivation", + "category": [ + "qdisc", + "hfsc" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc replace dev $DUMMY root handle 1: hfsc default 1", + "$TC class replace dev $DUMMY parent 1: classid 1:1 hfsc rt m1 32gbit d 1ms m2 0bit ls m1 32gbit d 1ms m2 0bit", + "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true", + "sleep 1" + ], + "cmdUnderTest": "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc hfsc 1: root", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] } ] From eeee5a710f26ce57807024ef330fe5a850eaecd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 26 Mar 2026 13:20:38 +0100 Subject: [PATCH 176/700] net: sfp: Fix Ubiquiti U-Fiber Instant SFP module on mvneta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override Autoneg and pause bits") we moved the setting of Autoneg and pause bits before the call to SFP quirk when parsing SFP module support. Since the quirk for Ubiquiti U-Fiber Instant SFP module zeroes the support bits and sets 1000baseX_Full only, the above mentioned commit changed the overall computed support from 1000baseX_Full, Autoneg, Pause, Asym_Pause to just 1000baseX_Full. This broke the SFP module for mvneta, which requires Autoneg for 1000baseX since commit c762b7fac1b249a9 ("net: mvneta: deny disabling autoneg for 802.3z modes"). Fix this by setting back the Autoneg, Pause and Asym_Pause bits in the quirk. Fixes: 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override Autoneg and pause bits") Signed-off-by: Marek Behún Reviewed-by: Russell King (Oracle) Link: https://patch.msgid.link/20260326122038.2489589-1-kabel@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 5db8413771990c..ce8924613363b1 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -480,11 +480,16 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, { /* Ubiquiti U-Fiber Instant module claims that support all transceiver * types including 10G Ethernet which is not truth. So clear all claimed - * modes and set only one mode which module supports: 1000baseX_Full. + * modes and set only one mode which module supports: 1000baseX_Full, + * along with the Autoneg and pause bits. */ linkmode_zero(caps->link_modes); linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, caps->link_modes); + phy_interface_zero(caps->interfaces); __set_bit(PHY_INTERFACE_MODE_1000BASEX, caps->interfaces); } From d389954a6cae7bf76b7b082ac3511d177b77ef2d Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 26 Mar 2026 15:52:32 +0800 Subject: [PATCH 177/700] net: enetc: check whether the RSS algorithm is Toeplitz Both ENETC v1 and v4 only provide Toeplitz RSS support. This patch adds a validation check to reject attempts to configure other RSS algorithms, avoiding misleading configuration options for users. Fixes: d382563f541b ("enetc: Add RFS and RSS support") Signed-off-by: Wei Fang Reviewed-by: Clark Wang Reviewed-by: Claudiu Manoil Link: https://patch.msgid.link/20260326075233.3628047-2-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 2fe140ddebb23b..a393647e6062cb 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -795,6 +795,10 @@ static int enetc_set_rxfh(struct net_device *ndev, struct enetc_si *si = priv->si; int err = 0; + if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && + rxfh->hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + /* set hash key, if PF */ if (rxfh->key && enetc_si_is_pf(si)) enetc_set_rss_key(si, rxfh->key); From a142d139168cce8d5776245b5494c7f7f5d7fb7d Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Thu, 26 Mar 2026 15:52:33 +0800 Subject: [PATCH 178/700] net: enetc: do not allow VF to configure the RSS key VFs do not have privilege to configure the RSS key because the registers are owned by the PF. Currently, if VF attempts to configure the RSS key, enetc_set_rxfh() simply skips the configuration and does not generate a warning, which may mislead users into thinking the feature is supported. To improve this situation, add a check to reject RSS key configuration on VFs. Fixes: d382563f541b ("enetc: Add RFS and RSS support") Signed-off-by: Wei Fang Reviewed-by: Clark Wang Reviewed-by: Claudiu Manoil Link: https://patch.msgid.link/20260326075233.3628047-3-wei.fang@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/enetc/enetc_ethtool.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index a393647e6062cb..7c17acaf7a380b 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -800,8 +800,12 @@ static int enetc_set_rxfh(struct net_device *ndev, return -EOPNOTSUPP; /* set hash key, if PF */ - if (rxfh->key && enetc_si_is_pf(si)) + if (rxfh->key) { + if (!enetc_si_is_pf(si)) + return -EOPNOTSUPP; + enetc_set_rss_key(si, rxfh->key); + } /* set RSS table */ if (rxfh->indir) From 45065a5095c7773fb98c35d60c20c3b513540597 Mon Sep 17 00:00:00 2001 From: Akshai Murari Date: Fri, 27 Mar 2026 06:54:45 +0000 Subject: [PATCH 179/700] Input: add keycodes for contextual AI usages (HUTRR119) HUTRR119 introduces new usages for keys intended to invoke AI agents based on the current context. These are useful with the increasing number of operating systems with integrated Large Language Models Add new key definitions for KEY_ACTION_ON_SELECTION, KEY_CONTEXTUAL_INSERT and KEY_CONTEXTUAL_QUERY Signed-off-by: Akshai Murari Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 6 ++++++ drivers/hid/hid-input.c | 3 +++ include/uapi/linux/input-event-codes.h | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index c5865b0d2aaaf6..a8d2b36a78525e 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -990,6 +990,9 @@ static const struct hid_usage_entry hid_usage_table[] = { { 0x0c, 0x01c9, "ALContactSync" }, { 0x0c, 0x01ca, "ALNavigation" }, { 0x0c, 0x01cb, "ALContextawareDesktopAssistant" }, + { 0x0c, 0x01cc, "ALActionOnSelection" }, + { 0x0c, 0x01cd, "ALContextualInsertion" }, + { 0x0c, 0x01ce, "ALContextualQuery" }, { 0x0c, 0x0200, "GenericGUIApplicationControls" }, { 0x0c, 0x0201, "ACNew" }, { 0x0c, 0x0202, "ACOpen" }, @@ -3375,6 +3378,9 @@ static const char *keys[KEY_MAX + 1] = { [KEY_BRIGHTNESS_MIN] = "BrightnessMin", [KEY_BRIGHTNESS_MAX] = "BrightnessMax", [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto", + [KEY_ACTION_ON_SELECTION] = "ActionOnSelection", + [KEY_CONTEXTUAL_INSERT] = "ContextualInsert", + [KEY_CONTEXTUAL_QUERY] = "ContextualQuery", [KEY_KBDINPUTASSIST_PREV] = "KbdInputAssistPrev", [KEY_KBDINPUTASSIST_NEXT] = "KbdInputAssistNext", [KEY_KBDINPUTASSIST_PREVGROUP] = "KbdInputAssistPrevGroup", diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8fc20df99b9768..ce9c3251287d2b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1227,6 +1227,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1bc: map_key_clear(KEY_MESSENGER); break; case 0x1bd: map_key_clear(KEY_INFO); break; case 0x1cb: map_key_clear(KEY_ASSISTANT); break; + case 0x1cc: map_key_clear(KEY_ACTION_ON_SELECTION); break; + case 0x1cd: map_key_clear(KEY_CONTEXTUAL_INSERT); break; + case 0x1ce: map_key_clear(KEY_CONTEXTUAL_QUERY); break; case 0x201: map_key_clear(KEY_NEW); break; case 0x202: map_key_clear(KEY_OPEN); break; case 0x203: map_key_clear(KEY_CLOSE); break; diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 4bdb6a1659873d..3528168f7c6d70 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -643,6 +643,10 @@ #define KEY_EPRIVACY_SCREEN_ON 0x252 #define KEY_EPRIVACY_SCREEN_OFF 0x253 +#define KEY_ACTION_ON_SELECTION 0x254 /* AL Action on Selection (HUTRR119) */ +#define KEY_CONTEXTUAL_INSERT 0x255 /* AL Contextual Insertion (HUTRR119) */ +#define KEY_CONTEXTUAL_QUERY 0x256 /* AL Contextual Query (HUTRR119) */ + #define KEY_KBDINPUTASSIST_PREV 0x260 #define KEY_KBDINPUTASSIST_NEXT 0x261 #define KEY_KBDINPUTASSIST_PREVGROUP 0x262 From b948f9d5d3057b01188e36664e7c7604d1c8ecb5 Mon Sep 17 00:00:00 2001 From: Junxi Qian Date: Sun, 29 Mar 2026 23:39:09 +0800 Subject: [PATCH 180/700] io_uring/net: fix slab-out-of-bounds read in io_bundle_nbufs() sqe->len is __u32 but gets stored into sr->len which is int. When userspace passes sqe->len values exceeding INT_MAX (e.g. 0xFFFFFFFF), sr->len overflows to a negative value. This negative value propagates through the bundle recv/send path: 1. io_recv(): sel.val = sr->len (ssize_t gets -1) 2. io_recv_buf_select(): arg.max_len = sel->val (size_t gets 0xFFFFFFFFFFFFFFFF) 3. io_ring_buffers_peek(): buf->len is not clamped because max_len is astronomically large 4. iov[].iov_len = 0xFFFFFFFF flows into io_bundle_nbufs() 5. io_bundle_nbufs(): min_t(int, 0xFFFFFFFF, ret) yields -1, causing ret to increase instead of decrease, creating an infinite loop that reads past the allocated iov[] array This results in a slab-out-of-bounds read in io_bundle_nbufs() from the kmalloc-64 slab, as nbufs increments past the allocated iovec entries. BUG: KASAN: slab-out-of-bounds in io_bundle_nbufs+0x128/0x160 Read of size 8 at addr ffff888100ae05c8 by task exp/145 Call Trace: io_bundle_nbufs+0x128/0x160 io_recv_finish+0x117/0xe20 io_recv+0x2db/0x1160 Fix this by rejecting negative sr->len values early in both io_sendmsg_prep() and io_recvmsg_prep(). Since sqe->len is __u32, any value > INT_MAX indicates overflow and is not a valid length. Fixes: a05d1f625c7a ("io_uring/net: support bundles for send") Cc: stable@vger.kernel.org Signed-off-by: Junxi Qian Link: https://patch.msgid.link/20260329153909.279046-1-qjx1298677004@gmail.com Signed-off-by: Jens Axboe --- io_uring/net.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_uring/net.c b/io_uring/net.c index d27adbe3f20be5..8885d944130a11 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -421,6 +421,8 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->done_io = 0; sr->len = READ_ONCE(sqe->len); + if (unlikely(sr->len < 0)) + return -EINVAL; sr->flags = READ_ONCE(sqe->ioprio); if (sr->flags & ~SENDMSG_FLAGS) return -EINVAL; @@ -791,6 +793,8 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr)); sr->len = READ_ONCE(sqe->len); + if (unlikely(sr->len < 0)) + return -EINVAL; sr->flags = READ_ONCE(sqe->ioprio); if (sr->flags & ~RECVMSG_FLAGS) return -EINVAL; From 111a12b422a8cfa93deabaef26fec48237163214 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Mon, 30 Mar 2026 00:49:36 +0800 Subject: [PATCH 181/700] io_uring/rsrc: reject zero-length fixed buffer import validate_fixed_range() admits buf_addr at the exact end of the registered region when len is zero, because the check uses strict greater-than (buf_end > imu->ubuf + imu->len). io_import_fixed() then computes offset == imu->len, which causes the bvec skip logic to advance past the last bio_vec entry and read bv_offset from out-of-bounds slab memory. Return early from io_import_fixed() when len is zero. A zero-length import has no data to transfer and should not walk the bvec array at all. BUG: KASAN: slab-out-of-bounds in io_import_reg_buf+0x697/0x7f0 Read of size 4 at addr ffff888002bcc254 by task poc/103 Call Trace: io_import_reg_buf+0x697/0x7f0 io_write_fixed+0xd9/0x250 __io_issue_sqe+0xad/0x710 io_issue_sqe+0x7d/0x1100 io_submit_sqes+0x86a/0x23c0 __do_sys_io_uring_enter+0xa98/0x1590 Allocated by task 103: The buggy address is located 12 bytes to the right of allocated 584-byte region [ffff888002bcc000, ffff888002bcc248) Fixes: 8622b20f23ed ("io_uring: add validate_fixed_range() for validate fixed buffer") Signed-off-by: Qi Tang Link: https://patch.msgid.link/20260329164936.240871-1-tpluszz77@gmail.com Signed-off-by: Jens Axboe --- io_uring/rsrc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 842e231c8a7c41..3f82ed7dd11ec6 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1062,6 +1062,10 @@ static int io_import_fixed(int ddir, struct iov_iter *iter, return ret; if (!(imu->dir & (1 << ddir))) return -EFAULT; + if (unlikely(!len)) { + iov_iter_bvec(iter, ddir, NULL, 0, 0); + return 0; + } offset = buf_addr - imu->ubuf; From 0e211f6aaa6a00fd0ee0c1eea5498f168c6725e6 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:40 +0000 Subject: [PATCH 182/700] hwmon: (tps53679) Fix array access with zero-length block read i2c_smbus_read_block_data() can return 0, indicating a zero-length read. When this happens, tps53679_identify_chip() accesses buf[ret - 1] which is buf[-1], reading one byte before the buffer on the stack. Fix by changing the check from "ret < 0" to "ret <= 0", treating a zero-length read as an error (-EIO), which prevents the out-of-bounds array access. Also fix a typo in the adjacent comment: "if present" instead of duplicate "if". Fixes: 75ca1e5875fe ("hwmon: (pmbus/tps53679) Add support for TPS53685") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-2-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index ca2bfa25eb04cc..3bca543817a605 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -103,10 +103,10 @@ static int tps53679_identify_chip(struct i2c_client *client, } ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); - if (ret < 0) - return ret; + if (ret <= 0) + return ret < 0 ? ret : -EIO; - /* Adjust length if null terminator if present */ + /* Adjust length if null terminator is present */ buf_len = (buf[ret - 1] != '\x00' ? ret : ret - 1); id_len = strlen(id); From ccf70c41e562b29d1c05d1bbf53391785e09c6fb Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:48 +0000 Subject: [PATCH 183/700] hwmon: (pxe1610) Check return value of page-select write in probe pxe1610_probe() writes PMBUS_PAGE to select page 0 but does not check the return value. If the write fails, subsequent register reads operate on an indeterminate page, leading to silent misconfiguration. Check the return value and propagate the error using dev_err_probe(), which also handles -EPROBE_DEFER correctly without log spam. Fixes: 344757bac526 ("hwmon: (pmbus) Add Infineon PXE1610 VR driver") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-4-sanman.pradhan@hpe.com [groeck: Fix "Fixes" SHA] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pxe1610.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 6a4a978eca7e89..24c1f961c76689 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -104,7 +104,10 @@ static int pxe1610_probe(struct i2c_client *client) * By default this device doesn't boot to page 0, so set page 0 * to access all pmbus registers. */ - i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to set page 0\n"); /* Read Manufacturer id */ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); From a9d2fbd3ad0e6ac588386e699beeccfe7516755f Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 29 Mar 2026 17:09:53 +0000 Subject: [PATCH 184/700] hwmon: (ltc4286) Add missing MODULE_IMPORT_NS("PMBUS") ltc4286.c uses PMBus core symbols exported in the PMBUS namespace, such as pmbus_do_probe(), but does not declare MODULE_IMPORT_NS("PMBUS"). Add the missing namespace import to avoid modpost warnings. Fixes: 0c459759ca97 ("hwmon: (pmbus) Add ltc4286 driver") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260329170925.34581-5-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ltc4286.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c index aabd0bcdfeee39..8715d380784a02 100644 --- a/drivers/hwmon/pmbus/ltc4286.c +++ b/drivers/hwmon/pmbus/ltc4286.c @@ -173,3 +173,4 @@ module_i2c_driver(ltc4286_driver); MODULE_AUTHOR("Delphine CC Chiu "); MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From fda9522ed6afaec45cabc198d8492270c394c7bc Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Wed, 25 Mar 2026 09:14:22 +0900 Subject: [PATCH 185/700] ksmbd: fix OOB write in QUERY_INFO for compound requests When a compound request such as READ + QUERY_INFO(Security) is received, and the first command (READ) consumes most of the response buffer, ksmbd could write beyond the allocated buffer while building a security descriptor. The root cause was that smb2_get_info_sec() checked buffer space using ppntsd_size from xattr, while build_sec_desc() often synthesized a significantly larger descriptor from POSIX ACLs. This patch introduces smb_acl_sec_desc_scratch_len() to accurately compute the final descriptor size beforehand, performs proper buffer checking with smb2_calc_max_out_buf_len(), and uses exact-sized allocation + iov pinning. Cc: stable@vger.kernel.org Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") Signed-off-by: Asim Viladi Oglu Manizada Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 121 +++++++++++++++++++++++++++++----------- fs/smb/server/smbacl.c | 43 ++++++++++++++ fs/smb/server/smbacl.h | 2 + 3 files changed, 134 insertions(+), 32 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6fb7a795ff5dec..8e4cfdc0ba025a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3402,20 +3402,24 @@ int smb2_open(struct ksmbd_work *work) KSMBD_SHARE_FLAG_ACL_XATTR)) { struct smb_fattr fattr; struct smb_ntsd *pntsd; - int pntsd_size, ace_num = 0; + int pntsd_size; + size_t scratch_len; ksmbd_acls_fattr(&fattr, idmap, inode); - if (fattr.cf_acls) - ace_num = fattr.cf_acls->a_count; - if (fattr.cf_dacls) - ace_num += fattr.cf_dacls->a_count; - - pntsd = kmalloc(sizeof(struct smb_ntsd) + - sizeof(struct smb_sid) * 3 + - sizeof(struct smb_acl) + - sizeof(struct smb_ace) * ace_num * 2, - KSMBD_DEFAULT_GFP); + scratch_len = smb_acl_sec_desc_scratch_len(&fattr, + NULL, 0, + OWNER_SECINFO | GROUP_SECINFO | + DACL_SECINFO); + if (!scratch_len || scratch_len == SIZE_MAX) { + rc = -EFBIG; + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + goto err_out; + } + + pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP); if (!pntsd) { + rc = -ENOMEM; posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); goto err_out; @@ -3430,7 +3434,7 @@ int smb2_open(struct ksmbd_work *work) posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); if (rc) { - kfree(pntsd); + kvfree(pntsd); goto err_out; } @@ -3440,7 +3444,7 @@ int smb2_open(struct ksmbd_work *work) pntsd, pntsd_size, false); - kfree(pntsd); + kvfree(pntsd); if (rc) pr_err("failed to store ntacl in xattr : %d\n", rc); @@ -5372,8 +5376,9 @@ static int smb2_get_info_file(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp, + rc = smb2_get_info_file_pipe(work->sess, req, rsp, work->response_buf); + goto iov_pin_out; } if (work->next_smb2_rcv_hdr_off) { @@ -5473,6 +5478,12 @@ static int smb2_get_info_file(struct ksmbd_work *work, rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), rsp, work->response_buf); ksmbd_fd_put(work, fp); + +iov_pin_out: + if (!rc) + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer) + + le32_to_cpu(rsp->OutputBufferLength)); return rc; } @@ -5699,6 +5710,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), rsp, work->response_buf); path_put(&path); + + if (!rc) + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer) + + le32_to_cpu(rsp->OutputBufferLength)); return rc; } @@ -5708,13 +5724,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work, { struct ksmbd_file *fp; struct mnt_idmap *idmap; - struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_ntsd *pntsd = NULL, *ppntsd = NULL; struct smb_fattr fattr = {{0}}; struct inode *inode; __u32 secdesclen = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; int addition_info = le32_to_cpu(req->AdditionalInformation); - int rc = 0, ppntsd_size = 0; + int rc = 0, ppntsd_size = 0, max_len; + size_t scratch_len = 0; if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | PROTECTED_DACL_SECINFO | @@ -5722,6 +5739,11 @@ static int smb2_get_info_sec(struct ksmbd_work *work, ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", addition_info); + pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8), + KSMBD_DEFAULT_GFP); + if (!pntsd) + return -ENOMEM; + pntsd->revision = cpu_to_le16(1); pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); pntsd->osidoffset = 0; @@ -5730,9 +5752,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, pntsd->dacloffset = 0; secdesclen = sizeof(struct smb_ntsd); - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - - return 0; + goto iov_pin; } if (work->next_smb2_rcv_hdr_off) { @@ -5764,18 +5784,58 @@ static int smb2_get_info_sec(struct ksmbd_work *work, &ppntsd); /* Check if sd buffer size exceeds response buffer size */ - if (smb2_resp_buf_len(work, 8) > ppntsd_size) - rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size, - addition_info, &secdesclen, &fattr); + max_len = smb2_calc_max_out_buf_len(work, + offsetof(struct smb2_query_info_rsp, Buffer), + le32_to_cpu(req->OutputBufferLength)); + if (max_len < 0) { + rc = -EINVAL; + goto release_acl; + } + + scratch_len = smb_acl_sec_desc_scratch_len(&fattr, ppntsd, + ppntsd_size, addition_info); + if (!scratch_len || scratch_len == SIZE_MAX) { + rc = -EFBIG; + goto release_acl; + } + + pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP); + if (!pntsd) { + rc = -ENOMEM; + goto release_acl; + } + + rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); + +release_acl: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); kfree(ppntsd); ksmbd_fd_put(work, fp); + + if (!rc && ALIGN(secdesclen, 8) > scratch_len) + rc = -EFBIG; if (rc) - return rc; + goto err_out; +iov_pin: rsp->OutputBufferLength = cpu_to_le32(secdesclen); - return 0; + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf); + if (rc) + goto err_out; + + rc = ksmbd_iov_pin_rsp_read(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer), + pntsd, secdesclen); +err_out: + if (rc) { + rsp->OutputBufferLength = 0; + kvfree(pntsd); + } + + return rc; } /** @@ -5799,6 +5859,9 @@ int smb2_query_info(struct ksmbd_work *work) goto err_out; } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); @@ -5819,14 +5882,6 @@ int smb2_query_info(struct ksmbd_work *work) } ksmbd_revert_fsids(work); - if (!rc) { - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - rc = ksmbd_iov_pin_rsp(work, (void *)rsp, - offsetof(struct smb2_query_info_rsp, Buffer) + - le32_to_cpu(rsp->OutputBufferLength)); - } - err_out: if (rc < 0) { if (rc == -EACCES) @@ -5837,6 +5892,8 @@ int smb2_query_info(struct ksmbd_work *work) rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; else if (rc == -ENOMEM) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EINVAL && rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; smb2_set_err_rsp(work); diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 49c2abb29bf5b2..c30d01877c4185 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -915,6 +915,49 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, return 0; } +size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info) +{ + size_t len = sizeof(struct smb_ntsd); + size_t tmp; + + if (addition_info & OWNER_SECINFO) + len += sizeof(struct smb_sid); + if (addition_info & GROUP_SECINFO) + len += sizeof(struct smb_sid); + if (!(addition_info & DACL_SECINFO)) + return len; + + len += sizeof(struct smb_acl); + if (ppntsd && ppntsd_size > 0) { + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + + if (dacl_offset < ppntsd_size && + check_add_overflow(len, ppntsd_size - dacl_offset, &len)) + return 0; + } + + if (fattr->cf_acls) { + if (check_mul_overflow((size_t)fattr->cf_acls->a_count, + 2 * sizeof(struct smb_ace), &tmp) || + check_add_overflow(len, tmp, &len)) + return 0; + } else { + /* default/minimum DACL */ + if (check_add_overflow(len, 5 * sizeof(struct smb_ace), &len)) + return 0; + } + + if (fattr->cf_dacls) { + if (check_mul_overflow((size_t)fattr->cf_dacls->a_count, + sizeof(struct smb_ace), &tmp) || + check_add_overflow(len, tmp, &len)) + return 0; + } + + return len; +} + /* Convert permission bits from mode to equivalent CIFS ACL */ int build_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h index 355adaee39b871..ab21ba2cd4df35 100644 --- a/fs/smb/server/smbacl.h +++ b/fs/smb/server/smbacl.h @@ -101,6 +101,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, bool type_check, bool get_write); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); +size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info); static inline uid_t posix_acl_uid_translate(struct mnt_idmap *idmap, struct posix_acl_entry *pace) From 742de64b62b690a368dbeb846499eb8ac8ceedb9 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Fri, 27 Mar 2026 09:30:29 +0100 Subject: [PATCH 186/700] kbuild: modules-cpio-pkg: Respect INSTALL_MOD_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modules-cpio-pkg target added in commit 2a9c8c0b59d3 ("kbuild: add target to build a cpio containing modules") is incompatible with initramfs with merged /lib and /usr/lib directories [1]. "/lib" cannot be a link and directory at the same time. Respect a non-empty INSTALL_MOD_PATH in the modules-cpio-pkg target so that `make INSTALL_MOD_PATH=/usr modules-cpio-pkg` results in the same module install location as `make INSTALL_MOD_PATH=/usr modules_install`. Tested with Fedora distribution initramfs produced by dracut. Link: https://systemd.io/THE_CASE_FOR_THE_USR_MERGE/ [1] Fixes: 2a9c8c0b59d3 ("kbuild: add target to build a cpio containing modules") Cc: stable@vger.kernel.org Reviewed-by: Simon Glass Reviewed-by: Nathan Chancellor Reviewed-by: Thomas Weißschuh Signed-off-by: Janne Grunau Reviewed-by: Nicolas Schier Tested-by: Nicolas Schier Reviewed-by: Ahmad Fatoum Link: https://patch.msgid.link/20260327-kbuild-modules-cpio-pkg-usr-merge-v3-1-ef507dfa006c@jannau.net Signed-off-by: Nathan Chancellor --- scripts/Makefile.package | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 0ec946f9b905f7..6d36786ba31cef 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -195,7 +195,7 @@ tar%-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar.% FORCE .tmp_modules_cpio: FORCE $(Q)$(MAKE) -f $(srctree)/Makefile $(Q)rm -rf $@ - $(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@ modules_install + $(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@/$(INSTALL_MOD_PATH) modules_install quiet_cmd_cpio = CPIO $@ cmd_cpio = $(CONFIG_SHELL) $(srctree)/usr/gen_initramfs.sh -o $@ $< @@ -264,6 +264,7 @@ help: @echo ' tarxz-pkg - Build the kernel as a xz compressed tarball' @echo ' tarzst-pkg - Build the kernel as a zstd compressed tarball' @echo ' modules-cpio-pkg - Build the kernel modules as cpio archive' + @echo ' (uses INSTALL_MOD_PATH inside the archive)' @echo ' perf-tar-src-pkg - Build the perf source tarball with no compression' @echo ' perf-targz-src-pkg - Build the perf source tarball with gzip compression' @echo ' perf-tarbz2-src-pkg - Build the perf source tarball with bz2 compression' From deb4605671cfae3b2803cfbbf4739e7245248398 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 25 Mar 2026 18:20:30 -0700 Subject: [PATCH 187/700] modpost: Declare extra_warn with unused attribute A recent strengthening of -Wunused-but-set-variable (enabled with -Wall) in clang under a new subwarning, -Wunused-but-set-global, points out an unused static global variable in scripts/mod/modpost.c: scripts/mod/modpost.c:59:13: error: variable 'extra_warn' set but not used [-Werror,-Wunused-but-set-global] 59 | static bool extra_warn; | ^ This variable has been unused since commit 6c6c1fc09de3 ("modpost: require a MODULE_DESCRIPTION()") but that is expected, as there are currently no extra warnings at W=1 right now. Declare the variable with the unused attribute to make it clear to the compiler that this variable may be unused. Cc: stable@vger.kernel.org Fixes: 6c6c1fc09de3 ("modpost: require a MODULE_DESCRIPTION()") Link: https://patch.msgid.link/20260325-modpost-extra_warn-unused-but-set-global-v1-1-2e84003b7e81@kernel.org Reviewed-by: Nicolas Schier Signed-off-by: Nathan Chancellor --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 0c25b5ad497b58..c3bc801d8b2de1 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -56,7 +56,7 @@ static bool allow_missing_ns_imports; static bool error_occurred; -static bool extra_warn; +static bool extra_warn __attribute__((unused)); bool target_is_big_endian; bool host_is_big_endian; From 71a98248c63c535eaa4d4c22f099b68d902006d0 Mon Sep 17 00:00:00 2001 From: Yasuaki Torimaru Date: Thu, 26 Mar 2026 14:58:00 +0900 Subject: [PATCH 188/700] xfrm: clear trailing padding in build_polexpire() build_expire() clears the trailing padding bytes of struct xfrm_user_expire after setting the hard field via memset_after(), but the analogous function build_polexpire() does not do this for struct xfrm_user_polexpire. The padding bytes after the __u8 hard field are left uninitialized from the heap allocation, and are then sent to userspace via netlink multicast to XFRMNLGRP_EXPIRE listeners, leaking kernel heap memory contents. Add the missing memset_after() call, matching build_expire(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Yasuaki Torimaru Reviewed-by: Simon Horman Reviewed-by: Breno Leitao Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 1656b487f8334e..5d59c11fc01efa 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3960,6 +3960,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, return err; } upe->hard = !!hard; + /* clear the padding bytes */ + memset_after(upe, 0, hard); nlmsg_end(skb, nlh); return 0; From 7081d46d32312f1a31f0e0e99c6835a394037599 Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Thu, 26 Mar 2026 20:36:39 +0800 Subject: [PATCH 189/700] xfrm: account XFRMA_IF_ID in aevent size calculation xfrm_get_ae() allocates the reply skb with xfrm_aevent_msgsize(), then build_aevent() appends attributes including XFRMA_IF_ID when x->if_id is set. xfrm_aevent_msgsize() does not include space for XFRMA_IF_ID. For states with if_id, build_aevent() can fail with -EMSGSIZE and hit BUG_ON(err < 0) in xfrm_get_ae(), turning a malformed netlink interaction into a kernel panic. Account XFRMA_IF_ID in the size calculation unconditionally and replace the BUG_ON with normal error unwinding. Fixes: 7e6526404ade ("xfrm: Add a new lookup key to match xfrm interfaces.") Reported-by: Keenan Dong Signed-off-by: Keenan Dong Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 5d59c11fc01efa..a779590c985a54 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2677,7 +2677,8 @@ static inline unsigned int xfrm_aevent_msgsize(struct xfrm_state *x) + nla_total_size(4) /* XFRM_AE_RTHR */ + nla_total_size(4) /* XFRM_AE_ETHR */ + nla_total_size(sizeof(x->dir)) /* XFRMA_SA_DIR */ - + nla_total_size(4); /* XFRMA_SA_PCPU */ + + nla_total_size(4) /* XFRMA_SA_PCPU */ + + nla_total_size(sizeof(x->if_id)); /* XFRMA_IF_ID */ } static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) @@ -2789,7 +2790,12 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, c.portid = nlh->nlmsg_pid; err = build_aevent(r_skb, x, &c); - BUG_ON(err < 0); + if (err < 0) { + spin_unlock_bh(&x->lock); + xfrm_state_put(x); + kfree_skb(r_skb); + return err; + } err = nlmsg_unicast(xfrm_net_nlsk(net, skb), r_skb, NETLINK_CB(skb).portid); spin_unlock_bh(&x->lock); From 4dfce79e098915d8e5fc2b9e1d980bc3251dd32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 26 Mar 2026 13:18:10 +0200 Subject: [PATCH 190/700] drm/i915/dsi: Don't do DSC horizontal timing adjustments in command mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop adjusting the horizontal timing values based on the compression ratio in command mode. Bspec seems to be telling us to do this only in video mode, and this is also how the Windows driver does things. This should also fix a div-by-zero on some machines because the adjusted htotal ends up being so small that we end up with line_time_us==0 when trying to determine the vtotal value in command mode. Note that this doesn't actually make the display on the Huawei Matebook E work, but at least the kernel no longer explodes when the driver loads. Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12045 Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260326111814.9800-2-ville.syrjala@linux.intel.com Fixes: 53693f02d80e ("drm/i915/dsi: account for DSC in horizontal timings") Reviewed-by: Jani Nikula (cherry picked from commit 0b475e91ecc2313207196c6d7fd5c53e1a878525) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/icl_dsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index fc265f71d72b90..298b3a48197c01 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -889,7 +889,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, * non-compressed link speeds, and simplifies down to the ratio between * compressed and non-compressed bpp. */ - if (crtc_state->dsc.compression_enable) { + if (is_vid_mode(intel_dsi) && crtc_state->dsc.compression_enable) { mul = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16); div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); } @@ -1503,7 +1503,7 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - if (pipe_config->dsc.compressed_bpp_x16) { + if (is_vid_mode(intel_dsi) && pipe_config->dsc.compressed_bpp_x16) { int div = fxp_q4_to_int(pipe_config->dsc.compressed_bpp_x16); int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); From 3d48c9fd78dd0b1809669ec49c4d0997b8127512 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Fri, 27 Mar 2026 17:41:56 +0500 Subject: [PATCH 191/700] dma-debug: suppress cacheline overlap warning when arch has no DMA alignment requirement When CONFIG_DMA_API_DEBUG is enabled, the DMA debug infrastructure tracks active mappings per cacheline and warns if two different DMA mappings share the same cacheline ("cacheline tracking EEXIST, overlapping mappings aren't supported"). On x86_64, ARCH_KMALLOC_MINALIGN defaults to 8, so small kmalloc allocations (e.g. the 8-byte hub->buffer and hub->status in the USB hub driver) frequently land in the same 64-byte cacheline. When both are DMA-mapped, this triggers a false positive warning. This has been reported repeatedly since v5.14 (when the EEXIST check was added) across various USB host controllers and devices including xhci_hcd with USB hubs, USB audio devices, and USB ethernet adapters. The cacheline overlap is only a real concern on architectures that require DMA buffer alignment to cacheline boundaries (i.e. where ARCH_DMA_MINALIGN >= L1_CACHE_BYTES). On architectures like x86_64 where dma_get_cache_alignment() returns 1, the hardware is cache-coherent and overlapping cacheline mappings are harmless. Suppress the EEXIST warning when dma_get_cache_alignment() is less than L1_CACHE_BYTES, indicating the architecture does not require cacheline-aligned DMA buffers. Verified with a kernel module reproducer that performs two kmalloc(8) allocations back-to-back and DMA-maps both: Before: allocations share a cacheline, EEXIST fires within ~50 pairs After: same cacheline pair found, but no warning emitted Fixes: 2b4bbc6231d7 ("dma-debug: report -EEXIST errors in add_dma_entry") Link: https://bugzilla.kernel.org/show_bug.cgi?id=215740 Suggested-by: Harry Yoo Tested-by: Mikhail Gavrilov Signed-off-by: Mikhail Gavrilov Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260327124156.24820-1-mikhail.v.gavrilov@gmail.com --- kernel/dma/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 0677918f06a80c..1a725edbbbf6ab 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -615,6 +615,7 @@ static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs) } else if (rc == -EEXIST && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !(entry->is_cache_clean && overlap_cache_clean) && + dma_get_cache_alignment() >= L1_CACHE_BYTES && !(IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && is_swiotlb_active(entry->dev))) { err_printk(entry->dev, entry, From 310a4a9cbb17037668ea440f6a3964d00705b400 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Mar 2026 12:06:38 +0100 Subject: [PATCH 192/700] gpio: shared: shorten the critical section in gpiochip_setup_shared() Commit 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set") introduced a critical section around the adjustmenet of entry->offset. However this may cause a deadlock if we create the auxiliary shared proxy devices with this lock taken. We only need to protect entry->offset while it's read/written so shorten the critical section and release the lock before creating the proxy device as the field in question is no longer accessed at this point. Fixes: 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set") Reported-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260325-gpio-shared-deadlock-v1-1-e4e7a5319e95@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-shared.c | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index e257212fa5e3df..e02d6b93a4ab42 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -533,48 +533,48 @@ int gpiochip_setup_shared(struct gpio_chip *gc) * exposing shared pins. Find them and create the proxy devices. */ list_for_each_entry(entry, &gpio_shared_list, list) { - guard(mutex)(&entry->lock); - if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; if (list_count_nodes(&entry->refs) <= 1) continue; + scoped_guard(mutex, &entry->lock) { #if IS_ENABLED(CONFIG_OF) - if (is_of_node(entry->fwnode) && gc->of_xlate) { - /* - * This is the earliest that we can tranlate the - * devicetree offset to the chip offset. - */ - struct of_phandle_args gpiospec = { }; + if (is_of_node(entry->fwnode) && gc->of_xlate) { + /* + * This is the earliest that we can tranlate the + * devicetree offset to the chip offset. + */ + struct of_phandle_args gpiospec = { }; - gpiospec.np = to_of_node(entry->fwnode); - gpiospec.args_count = 2; - gpiospec.args[0] = entry->offset; + gpiospec.np = to_of_node(entry->fwnode); + gpiospec.args_count = 2; + gpiospec.args[0] = entry->offset; - ret = gc->of_xlate(gc, &gpiospec, NULL); - if (ret < 0) - return ret; + ret = gc->of_xlate(gc, &gpiospec, NULL); + if (ret < 0) + return ret; - entry->offset = ret; - } + entry->offset = ret; + } #endif /* CONFIG_OF */ - desc = &gdev->descs[entry->offset]; + desc = &gdev->descs[entry->offset]; - __set_bit(GPIOD_FLAG_SHARED, &desc->flags); - /* - * Shared GPIOs are not requested via the normal path. Make - * them inaccessible to anyone even before we register the - * chip. - */ - ret = gpiod_request_commit(desc, "shared"); - if (ret) - return ret; + __set_bit(GPIOD_FLAG_SHARED, &desc->flags); + /* + * Shared GPIOs are not requested via the normal path. Make + * them inaccessible to anyone even before we register the + * chip. + */ + ret = gpiod_request_commit(desc, "shared"); + if (ret) + return ret; - pr_debug("GPIO %u owned by %s is shared by multiple consumers\n", - entry->offset, gpio_device_get_label(gdev)); + pr_debug("GPIO %u owned by %s is shared by multiple consumers\n", + entry->offset, gpio_device_get_label(gdev)); + } list_for_each_entry(ref, &entry->refs, list) { pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n", From 6b5ef8c88854b343b733b574ea8754c9dab61f41 Mon Sep 17 00:00:00 2001 From: Jamie Gibbons Date: Thu, 26 Mar 2026 17:02:34 +0000 Subject: [PATCH 193/700] dt-bindings: gpio: fix microchip #interrupt-cells The GPIO controller on PolarFire SoC supports more than one type of interrupt and needs two interrupt cells. Fixes: 735806d8a68e9 ("dt-bindings: gpio: add bindings for microchip mpfs gpio") Signed-off-by: Jamie Gibbons Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20260326-wise-gumdrop-49217723a72a@spud Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/microchip,mpfs-gpio.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml index 184432d24ea181..f42c54653d5217 100644 --- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml @@ -37,7 +37,7 @@ properties: const: 2 "#interrupt-cells": - const: 1 + const: 2 ngpios: description: @@ -86,7 +86,7 @@ examples: gpio-controller; #gpio-cells = <2>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, <53>, From 2f42c1a6161646cbd29b443459fd635d29eda634 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 27 Mar 2026 14:32:53 +0100 Subject: [PATCH 194/700] drm/ast: dp501: Fix initialization of SCU2C Ast's DP501 initialization reads the register SCU2C at offset 0x1202c and tries to set it to source data from VGA. But writes the update to offset 0x0, with unknown results. Write the result to SCU instead. The bug only happens in ast_init_analog(). There's similar code in ast_init_dvo(), which works correctly. Signed-off-by: Thomas Zimmermann Fixes: 83c6620bae3f ("drm/ast: initial DP501 support (v0.2)") Reviewed-by: Jocelyn Falempe Cc: Dave Airlie Cc: Thomas Zimmermann Cc: Jocelyn Falempe Cc: dri-devel@lists.freedesktop.org Cc: # v3.16+ Link: https://patch.msgid.link/20260327133532.79696-2-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_dp501.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 9e19d8c1773083..677c52c0d99a3c 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -436,7 +436,7 @@ static void ast_init_analog(struct ast_device *ast) /* Finally, clear bits [17:16] of SCU2c */ data = ast_read32(ast, 0x1202c); data &= 0xfffcffff; - ast_write32(ast, 0, data); + ast_write32(ast, 0x1202c, data); /* Disable DVO */ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00); From 917e3ad3321e75ca0223d5ccf26ceda116aa51e1 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 25 Mar 2026 16:48:24 +0100 Subject: [PATCH 195/700] x86/kexec: Disable KCOV instrumentation after load_segments() The load_segments() function changes segment registers, invalidating GS base (which KCOV relies on for per-cpu data). When CONFIG_KCOV is enabled, any subsequent instrumented C code call (e.g. native_gdt_invalidate()) begins crashing the kernel in an endless loop. To reproduce the problem, it's sufficient to do kexec on a KCOV-instrumented kernel: $ kexec -l /boot/otherKernel $ kexec -e The real-world context for this problem is enabling crash dump collection in syzkaller. For this, the tool loads a panic kernel before fuzzing and then calls makedumpfile after the panic. This workflow requires both CONFIG_KEXEC and CONFIG_KCOV to be enabled simultaneously. Adding safeguards directly to the KCOV fast-path (__sanitizer_cov_trace_pc()) is also undesirable as it would introduce an extra performance overhead. Disabling instrumentation for the individual functions would be too fragile, so disable KCOV instrumentation for the entire machine_kexec_64.c and physaddr.c. If coverage-guided fuzzing ever needs these components in the future, other approaches should be considered. The problem is not relevant for 32 bit kernels as CONFIG_KCOV is not supported there. [ bp: Space out comment for better readability. ] Fixes: 0d345996e4cb ("x86/kernel: increase kcov coverage under arch/x86/kernel folder") Signed-off-by: Aleksandr Nogikh Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Dmitry Vyukov Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325154825.551191-1-nogikh@google.com --- arch/x86/kernel/Makefile | 14 ++++++++++++++ arch/x86/mm/Makefile | 2 ++ 2 files changed, 16 insertions(+) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index e9aeeeafad173a..47a32f58393004 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -44,6 +44,20 @@ KCOV_INSTRUMENT_unwind_orc.o := n KCOV_INSTRUMENT_unwind_frame.o := n KCOV_INSTRUMENT_unwind_guess.o := n +# Disable KCOV to prevent crashes during kexec: load_segments() invalidates +# the GS base, which KCOV relies on for per-CPU data. +# +# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is +# using it to collect crash dumps during kernel fuzzing), disabling +# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV +# instrumentation for individual affected functions can be fragile, while +# adding more checks to KCOV would slow it down. +# +# As a compromise solution, disable KCOV instrumentation for the whole +# source code file. If its coverage is ever needed, other approaches +# should be considered. +KCOV_INSTRUMENT_machine_kexec_64.o := n + CFLAGS_head32.o := -fno-stack-protector CFLAGS_head64.o := -fno-stack-protector CFLAGS_irq.o := -I $(src)/../include/asm/trace diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 5b9908f13dcfd0..3a5364853eab87 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -4,6 +4,8 @@ KCOV_INSTRUMENT_tlb.o := n KCOV_INSTRUMENT_mem_encrypt.o := n KCOV_INSTRUMENT_mem_encrypt_amd.o := n KCOV_INSTRUMENT_pgprot.o := n +# See the "Disable KCOV" comment in arch/x86/kernel/Makefile. +KCOV_INSTRUMENT_physaddr.o := n KASAN_SANITIZE_mem_encrypt.o := n KASAN_SANITIZE_mem_encrypt_amd.o := n From 73cd1f97946ae3796544448ff12c07f399bb2881 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 19:14:05 +0800 Subject: [PATCH 196/700] spi: stm32-ospi: Fix resource leak in remove() callback The remove() callback returned early if pm_runtime_resume_and_get() failed, skipping the cleanup of spi controller and other resources. Remove the early return so cleanup completes regardless of PM resume result. Fixes: 79b8a705e26c ("spi: stm32: Add OSPI driver") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-ospi-v1-1-cc8cf1c82c4a@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index c98afe02a1b6b6..2baf651c0a6da8 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -989,11 +989,8 @@ static int stm32_ospi_probe(struct platform_device *pdev) static void stm32_ospi_remove(struct platform_device *pdev) { struct stm32_ospi *ospi = platform_get_drvdata(pdev); - int ret; - ret = pm_runtime_resume_and_get(ospi->dev); - if (ret < 0) - return; + pm_runtime_resume_and_get(ospi->dev); spi_unregister_controller(ospi->ctrl); /* Disable ospi */ From 5a570c8d6e55689253f6fcc4a198c56cca7e39d6 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 00:07:06 +0800 Subject: [PATCH 197/700] spi: stm32-ospi: Fix reset control leak on probe error When spi_register_controller() fails after reset_control_acquire() succeeds, the reset control is never released. This causes a resource leak in the error path. Add the missing reset_control_release() call in the error path. Fixes: cf2c3eceb757 ("spi: stm32-ospi: Make usage of reset_control_acquire/release() API") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-stm32-ospi-v1-1-142122466412@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 2baf651c0a6da8..c9f92e85253ffc 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -965,13 +965,15 @@ static int stm32_ospi_probe(struct platform_device *pdev) if (ret) { /* Disable ospi */ writel_relaxed(0, ospi->regs_base + OSPI_CR); - goto err_pm_resume; + goto err_reset_control; } pm_runtime_put_autosuspend(ospi->dev); return 0; +err_reset_control: + reset_control_release(ospi->rstc); err_pm_resume: pm_runtime_put_sync_suspend(ospi->dev); From 534025950c9fe4dfbe476b3938d73a26814047d1 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 29 Mar 2026 00:07:07 +0800 Subject: [PATCH 198/700] spi: stm32-ospi: Fix DMA channel leak on stm32_ospi_dma_setup() failure When stm32_ospi_dma_setup() fails, the DMA channels allocated by stm32_ospi_get_resources() were never released. Add proper cleanup in the error path. Fixes: e35a7607e05d ("spi: stm32-ospi: Set DMA maxburst dynamically") Signed-off-by: Felix Gu Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260329-stm32-ospi-v1-2-142122466412@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index c9f92e85253ffc..38405f8f547f56 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -928,7 +928,7 @@ static int stm32_ospi_probe(struct platform_device *pdev) dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR; ret = stm32_ospi_dma_setup(ospi, &dma_cfg); if (ret) - return ret; + goto err_dma_free; mutex_init(&ospi->lock); @@ -980,6 +980,7 @@ static int stm32_ospi_probe(struct platform_device *pdev) err_pm_enable: pm_runtime_force_suspend(ospi->dev); mutex_destroy(&ospi->lock); +err_dma_free: if (ospi->dma_chtx) dma_release_channel(ospi->dma_chtx); if (ospi->dma_chrx) From 6d192b4f2d644d15d9a9f1d33dab05af936f6540 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Tue, 24 Mar 2026 15:29:37 +0000 Subject: [PATCH 199/700] drm/xe/xe_pagefault: Disallow writes to read-only VMAs The page fault handler should reject write/atomic access to read only VMAs. Add code to handle this in xe_pagefault_service after the VMA lookup. v2: - Apply max line length (Matthew) Fixes: fb544b844508 ("drm/xe: Implement xe_pagefault_queue_work") Signed-off-by: Jonathan Cavitt Suggested-by: Matthew Brost Cc: Shuicheng Lin Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260324152935.72444-7-jonathan.cavitt@intel.com (cherry picked from commit 714ee6754ac5fa3dc078856a196a6b124cd797a0) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pagefault.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index 6bee53d6ffc374..922a4f3344b198 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -187,6 +187,12 @@ static int xe_pagefault_service(struct xe_pagefault *pf) goto unlock_vm; } + if (xe_vma_read_only(vma) && + pf->consumer.access_type != XE_PAGEFAULT_ACCESS_TYPE_READ) { + err = -EPERM; + goto unlock_vm; + } + atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type); if (xe_vma_is_cpu_addr_mirror(vma)) From 7b9a3a910e96f20b101b0df6b1f5c77b023a4097 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 26 Mar 2026 18:38:38 +0530 Subject: [PATCH 200/700] drm/xe/madvise: Accept canonical GPU addresses in xe_vm_madvise_ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace passes canonical (sign-extended) GPU addresses where bits 63:48 mirror bit 47. The internal GPUVM uses non-canonical form (upper bits zeroed), so passing raw canonical addresses into GPUVM lookups causes mismatches for addresses above 128TiB. Strip the sign extension with xe_device_uncanonicalize_addr() at the top of xe_vm_madvise_ioctl(). Non-canonical addresses are unaffected. Fixes: ada7486c5668 ("drm/xe: Implement madvise ioctl for xe") Suggested-by: Matthew Brost Cc: Thomas Hellström Reviewed-by: Matthew Brost Signed-off-by: Himal Prasad Ghimiray Signed-off-by: Arvind Yadav Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260326130843.3545241-13-arvind.yadav@intel.com (cherry picked from commit 05c8b1cdc54036465ea457a0501a8c2f9409fce7) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_vm_madvise.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index bc39a9a9790c3c..b4086129a3648c 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -408,8 +408,15 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil struct xe_device *xe = to_xe_device(dev); struct xe_file *xef = to_xe_file(file); struct drm_xe_madvise *args = data; - struct xe_vmas_in_madvise_range madvise_range = {.addr = args->start, - .range = args->range, }; + struct xe_vmas_in_madvise_range madvise_range = { + /* + * Userspace may pass canonical (sign-extended) addresses. + * Strip the sign extension to get the internal non-canonical + * form used by the GPUVM, matching xe_vm_bind_ioctl() behavior. + */ + .addr = xe_device_uncanonicalize_addr(xe, args->start), + .range = args->range, + }; struct xe_madvise_details details; struct xe_vm *vm; struct drm_exec exec; @@ -439,7 +446,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil if (err) goto unlock_vm; - err = xe_vm_alloc_madvise_vma(vm, args->start, args->range); + err = xe_vm_alloc_madvise_vma(vm, madvise_range.addr, args->range); if (err) goto madv_fini; @@ -482,7 +489,8 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, args->start, args->start + args->range); + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); From e2628e670bb0923fcdc00828bfcd67b26a7df020 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:20 -0700 Subject: [PATCH 201/700] drm/xe/pxp: Clean up termination status on failure If the PXP HW termination fails during PXP start, the normal completion code won't be called, so the termination will remain uncomplete. To avoid unnecessary waits, mark the termination as completed from the error path. Note that we already do this if the termination fails when handling a termination irq from the HW. Fixes: f8caa80154c4 ("drm/xe/pxp: Add PXP queue tracking and session start") Signed-off-by: Daniele Ceraolo Spurio Cc: Alan Previn Teres Alexis Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-7-daniele.ceraolospurio@intel.com (cherry picked from commit 5d9e708d2a69ab1f64a17aec810cd7c70c5b9fab) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index d61446bf9c19b6..782281dbe9e0a6 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -583,6 +583,7 @@ static int pxp_start(struct xe_pxp *pxp, u8 type) drm_err(&pxp->xe->drm, "PXP termination failed before start\n"); mutex_lock(&pxp->mutex); pxp->status = XE_PXP_ERROR; + complete_all(&pxp->termination); goto out_unlock; } From 4fed244954c2dc9aafa333d08f66b14345225e03 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:21 -0700 Subject: [PATCH 202/700] drm/xe/pxp: Remove incorrect handling of impossible state during suspend The default case of the PXP suspend switch is incorrectly exiting without releasing the lock. However, this case is impossible to hit because we're switching on an enum and all the valid enum values have their own cases. Therefore, we can just get rid of the default case and rely on the compiler to warn us if a new enum value is added and we forget to add it to the switch. Fixes: 51462211f4a9 ("drm/xe/pxp: add PXP PM support") Signed-off-by: Daniele Ceraolo Spurio Cc: Alan Previn Teres Alexis Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-8-daniele.ceraolospurio@intel.com (cherry picked from commit f1b5a77fc9b6a90cd9a5e3db9d4c73ae1edfcfac) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 782281dbe9e0a6..fa82d606953e5d 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -871,11 +871,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) pxp->key_instance++; needs_queue_inval = true; break; - default: - drm_err(&pxp->xe->drm, "unexpected state during PXP suspend: %u", - pxp->status); - ret = -EIO; - goto out; } /* @@ -900,7 +895,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) pxp->last_suspend_key_instance = pxp->key_instance; -out: return ret; } From 76903b2057c8677c2c006e87fede15f496555dc0 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:22 -0700 Subject: [PATCH 203/700] drm/xe/pxp: Clear restart flag in pxp_start after jumping back If we don't clear the flag we'll keep jumping back at the beginning of the function once we reach the end. Fixes: ccd3c6820a90 ("drm/xe/pxp: Decouple queue addition from PXP start") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Link: https://patch.msgid.link/20260324153718.3155504-9-daniele.ceraolospurio@intel.com (cherry picked from commit 0850ec7bb2459602351639dccf7a68a03c9d1ee0) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index fa82d606953e5d..088a1ab75f7017 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -512,7 +512,7 @@ static int __exec_queue_add(struct xe_pxp *pxp, struct xe_exec_queue *q) static int pxp_start(struct xe_pxp *pxp, u8 type) { int ret = 0; - bool restart = false; + bool restart; if (!xe_pxp_is_enabled(pxp)) return -ENODEV; @@ -541,6 +541,8 @@ static int pxp_start(struct xe_pxp *pxp, u8 type) msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) return -ETIMEDOUT; + restart = false; + mutex_lock(&pxp->mutex); /* If PXP is not already active, turn it on */ From e3fb579872a8d9cf264c52710d5839de3afa6fc1 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 24 Mar 2026 08:37:23 -0700 Subject: [PATCH 204/700] drm/xe/pxp: Don't allow PXP on older PTL GSC FWs On PTL, older GSC FWs have a bug that can cause them to crash during PXP invalidation events, which leads to a complete loss of power management on the media GT. Therefore, we can't use PXP on FWs that have this bug, which was fixed in PTL GSC build 1396. Fixes: b1dcec9bd8a1 ("drm/xe/ptl: Enable PXP for PTL") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Reviewed-by: Julia Filipchuk Acked-by: Rodrigo Vivi Link: https://patch.msgid.link/20260324153718.3155504-10-daniele.ceraolospurio@intel.com (cherry picked from commit 6eb04caaa972934c9b6cea0e0c29e466bf9a346f) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pxp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 088a1ab75f7017..872217f3037538 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -380,6 +380,18 @@ int xe_pxp_init(struct xe_device *xe) return 0; } + /* + * On PTL, older GSC FWs have a bug that can cause them to crash during + * PXP invalidation events, which leads to a complete loss of power + * management on the media GT. Therefore, we can't use PXP on FWs that + * have this bug, which was fixed in PTL GSC build 1396. + */ + if (xe->info.platform == XE_PANTHERLAKE && + gt->uc.gsc.fw.versions.found[XE_UC_FW_VER_RELEASE].build < 1396) { + drm_info(&xe->drm, "PXP requires PTL GSC build 1396 or newer\n"); + return 0; + } + pxp = drmm_kzalloc(&xe->drm, sizeof(struct xe_pxp), GFP_KERNEL); if (!pxp) { err = -ENOMEM; From bce7cd6db2386e7a2b49ad3c09df0beabddfa3c4 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 26 Feb 2026 17:52:25 -0800 Subject: [PATCH 205/700] drm/xe: Disable garbage collector work item on SVM close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an SVM is closed, the garbage collector work item must be stopped synchronously and any future queuing must be prevented. Replace flush_work() with disable_work_sync() to ensure both conditions are met. Fixes: 63f6e480d115 ("drm/xe: Add SVM garbage collector") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260227015225.3081787-1-matthew.brost@intel.com (cherry picked from commit 2247feb9badca5a4774df9a437bfc44fba4f22de) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index ca67d0bdbfac5a..c6b92d4ea6f43e 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -903,7 +903,7 @@ int xe_svm_init(struct xe_vm *vm) void xe_svm_close(struct xe_vm *vm) { xe_assert(vm->xe, xe_vm_is_closed(vm)); - flush_work(&vm->svm.garbage_collector.work); + disable_work_sync(&vm->svm.garbage_collector.work); xe_svm_put_pagemaps(vm); drm_pagemap_release_owner(&vm->svm.peer); } From 56b7432b7e8e6ae1b289cb405d16db4150ef193b Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 26 Mar 2026 14:01:15 -0700 Subject: [PATCH 206/700] drm/xe: Avoid memory allocations in xe_device_declare_wedged() xe_device_declare_wedged() runs in the DMA-fence signaling path, where GFP_KERNEL memory allocations are not allowed. However, registering xe_device_wedged_fini via drmm_add_action_or_reset() triggers a GFP_KERNEL allocation. Fix this by deferring the registration of xe_device_wedged_fini until late in the driver load sequence. Additionally, drop the wedged PM reference only if the device is actually wedged in xe_device_wedged_fini. Fixes: 452bca0edbd0 ("drm/xe: Don't suspend device upon wedge") Signed-off-by: Matthew Brost Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260326210116.202585-2-matthew.brost@intel.com (cherry picked from commit b08ceb443866808b881b12d4183008d214d816c1) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_device.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 52ee24e9cd3af2..3eb06b27db7eee 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -837,6 +837,14 @@ static void detect_preproduction_hw(struct xe_device *xe) } } +static void xe_device_wedged_fini(struct drm_device *drm, void *arg) +{ + struct xe_device *xe = arg; + + if (atomic_read(&xe->wedged.flag)) + xe_pm_runtime_put(xe); +} + int xe_device_probe(struct xe_device *xe) { struct xe_tile *tile; @@ -1013,6 +1021,10 @@ int xe_device_probe(struct xe_device *xe) detect_preproduction_hw(xe); + err = drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe); + if (err) + goto err_unregister_display; + return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); err_unregister_display: @@ -1216,13 +1228,6 @@ u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address) return address & GENMASK_ULL(xe->info.va_bits - 1, 0); } -static void xe_device_wedged_fini(struct drm_device *drm, void *arg) -{ - struct xe_device *xe = arg; - - xe_pm_runtime_put(xe); -} - /** * DOC: Xe Device Wedging * @@ -1300,15 +1305,9 @@ void xe_device_declare_wedged(struct xe_device *xe) return; } - xe_pm_runtime_get_noresume(xe); - - if (drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe)) { - drm_err(&xe->drm, "Failed to register xe_device_wedged_fini clean-up. Although device is wedged.\n"); - return; - } - if (!atomic_xchg(&xe->wedged.flag, 1)) { xe->needs_flr_on_fini = true; + xe_pm_runtime_get_noresume(xe); drm_err(&xe->drm, "CRITICAL: Xe has declared device %s as wedged.\n" "IOCTLs and executions are blocked. Only a rebind may clear the failure\n" From 11b72b1ca9891c77bc876ef9fc39d6825847ffee Mon Sep 17 00:00:00 2001 From: Ziyue Zhang Date: Mon, 30 Mar 2026 10:09:34 +0800 Subject: [PATCH 207/700] arm64: dts: qcom: hamoa: Fix incomplete Root Port property migration Historically, the Qualcomm PCIe controller node (Host bridge) described all Root Port properties, such as PHY, PERST#, and WAKE#. But to provide a more accurate hardware description and to support future multi-Root Port controllers, these properties were moved to the Root Port node in the devicetree bindings. Commit 960609b22be5 ("arm64: dts: qcom: hamoa: Move PHY, PERST, and Wake GPIOs to PCIe port nodes and add port Nodes for all PCIe ports") initiated this transition for the Hamoa platform by moving the PHY property to the Root Port node in hamoa.dtsi. However, it only updated some platform specific DTS files for PERST# and WAKE#, leaving others in a "mixed" binding state. While the PCIe controller driver supports both legacy and Root Port bindings, It cannot correctly handle a mix of both. In these cases, the driver parses the PHY from the Root Port node, but fails to find the PERST# property (which it then assumes is not present, as it is optional). Consequently, the controller probe succeeds, but PERST# remains uncontrolled, preventing PCIe endpoints from functioning. So, fix the incomplete migration by moving the PERST# and WAKE# properties from the controller node to the Root Port node in all remaining Hamoa platform DTS files. Fixes: 960609b22be5 ("arm64: dts: qcom: hamoa: Move PHY, PERST, and Wake GPIOs to PCIe port nodes and add port Nodes for all PCIe ports") Signed-off-by: Ziyue Zhang Reviewed-by: Manivannan Sadhasivam Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260330020934.3501247-1-ziyue.zhang@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../boot/dts/qcom/x1-asus-zenbook-a14.dtsi | 16 ++++++++----- arch/arm64/boot/dts/qcom/x1-crd.dtsi | 24 ++++++++++++------- arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi | 14 ++++++----- .../boot/dts/qcom/x1-hp-omnibook-x14.dtsi | 14 ++++++----- .../boot/dts/qcom/x1-microsoft-denali.dtsi | 8 ++++--- .../dts/qcom/x1e80100-lenovo-yoga-slim7x.dts | 6 ++--- .../qcom/x1e80100-medion-sprchrgd-14-s1.dts | 15 ++++++------ .../dts/qcom/x1p42100-lenovo-thinkbook-16.dts | 14 ++++++----- 8 files changed, 65 insertions(+), 46 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi index 8e5c5575a53201..0a382cc9e64315 100644 --- a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi @@ -1032,9 +1032,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1048,10 +1045,12 @@ status = "okay"; }; -&pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; +}; +&pcie6a { vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -1067,6 +1066,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pm8550_gpios { rtmr0_default: rtmr0-reset-n-active-state { pins = "gpio10"; diff --git a/arch/arm64/boot/dts/qcom/x1-crd.dtsi b/arch/arm64/boot/dts/qcom/x1-crd.dtsi index ded96fb43489b9..2fbf9ec66fb8bb 100644 --- a/arch/arm64/boot/dts/qcom/x1-crd.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-crd.dtsi @@ -1216,15 +1216,17 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; status = "okay"; }; +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; +}; + &pcie4_phy { vdda-phy-supply = <&vreg_l3i_0p8>; vdda-pll-supply = <&vreg_l3e_1p2>; @@ -1233,9 +1235,6 @@ }; &pcie5 { - perst-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_wwan>; pinctrl-0 = <&pcie5_default>; @@ -1251,10 +1250,12 @@ status = "okay"; }; -&pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +&pcie5_port0 { + reset-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; +}; +&pcie6a { vddpe-3v3-supply = <&vreg_nvme>; pinctrl-names = "default"; @@ -1270,6 +1271,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pm8550_gpios { kypd_vol_up_n: kypd-vol-up-n-state { pins = "gpio6"; diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi index bf04a12b16bc93..217ca8c7d81dae 100644 --- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi @@ -1081,9 +1081,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1098,6 +1095,9 @@ }; &pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + wifi@0 { compatible = "pci17cb,1107"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -1115,9 +1115,6 @@ }; &pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -1126,6 +1123,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pcie6a_phy { vdda-phy-supply = <&vreg_l1d_0p8>; vdda-pll-supply = <&vreg_l2j_1p2>; diff --git a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi index a4075434162aa0..41063948c583da 100644 --- a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi @@ -1065,9 +1065,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1082,6 +1079,9 @@ }; &pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + wifi@0 { compatible = "pci17cb,1107"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -1099,9 +1099,6 @@ }; &pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -1110,6 +1107,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pcie6a_phy { vdda-phy-supply = <&vreg_l1d_0p8>; vdda-pll-supply = <&vreg_l2j_1p2>; diff --git a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi index d77be02848b553..ba6b7b5a919186 100644 --- a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi @@ -964,9 +964,6 @@ }; &pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -982,6 +979,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pm8550_gpios { rtmr0_default: rtmr0-reset-n-active-state { pins = "gpio10"; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts index d6472e5a3f9fa7..d7938d3492053b 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts @@ -1126,9 +1126,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1143,6 +1140,9 @@ }; &pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + wifi@0 { compatible = "pci17cb,1107"; reg = <0x10000 0x0 0x0 0x0 0x0>; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts index 20a33e6f27ee32..eec5f2f1f75d67 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts @@ -1033,9 +1033,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1050,6 +1047,9 @@ }; &pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + wifi@0 { compatible = "pci17cb,1107"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -1067,10 +1067,6 @@ }; &pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -1086,6 +1082,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pm8550_gpios { rtmr0_default: rtmr0-reset-n-active-state { pins = "gpio10"; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts index 1e5eb8c5dc988c..06747b54a38e43 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts @@ -1131,9 +1131,6 @@ }; &pcie4 { - perst-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie4_default>; pinctrl-names = "default"; @@ -1148,6 +1145,9 @@ }; &pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + wifi@0 { compatible = "pci17cb,1107"; reg = <0x10000 0x0 0x0 0x0 0x0>; @@ -1165,9 +1165,6 @@ }; &pcie6a { - perst-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&vreg_nvme>; pinctrl-0 = <&pcie6a_default>; @@ -1183,6 +1180,11 @@ status = "okay"; }; +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + &pm8550_pwm { status = "okay"; }; From 679b771ea05ad0f8eeae83e14a91b8f4f39510c4 Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Wed, 18 Mar 2026 11:57:07 -0700 Subject: [PATCH 208/700] usb: ehci-brcm: fix sleep during atomic echi_brcm_wait_for_sof() gets called after disabling interrupts in ehci_brcm_hub_control(). Use the atomic version of poll_timeout to fix the warning. Fixes: 9df231511bd6 ("usb: ehci: Add new EHCI driver for Broadcom STB SoC's") Cc: stable Signed-off-by: Justin Chen Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260318185707.2588431-1-justin.chen@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-brcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 888e8f6670d2a1..5e3156f94cc690 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -31,8 +31,8 @@ static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay) int res; /* Wait for next microframe (every 125 usecs) */ - res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val, - val != frame_idx, 1, 130); + res = readl_relaxed_poll_timeout_atomic(&ehci->regs->frame_index, + val, val != frame_idx, 1, 130); if (res) ehci_err(ehci, "Error waiting for SOF\n"); udelay(delay); From dd36014ec6042f424ef51b923e607772f7502ee7 Mon Sep 17 00:00:00 2001 From: Miao Li Date: Thu, 19 Mar 2026 13:39:27 +0800 Subject: [PATCH 209/700] usb: quirks: add DELAY_INIT quirk for another Silicon Motion flash drive Another Silicon Motion flash drive also randomly work incorrectly (lsusb does not list the device) on Huawei hisi platforms during 500 reboot cycles, and the DELAY_INIT quirk fixes this issue. Signed-off-by: Miao Li Cc: stable Link: https://patch.msgid.link/20260319053927.264840-1-limiao870622@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 5523a8e2902173..26fed25da26edb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -401,6 +401,7 @@ static const struct usb_device_id usb_quirk_list[] = { /* Silicon Motion Flash Drive */ { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT }, /* Sound Devices USBPre2 */ { USB_DEVICE(0x0926, 0x0202), .driver_info = From eba2936bbe6b752a31725a9eb5c674ecbf21ee7d Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Fri, 20 Mar 2026 14:54:27 +0800 Subject: [PATCH 210/700] usb: gadget: uvc: fix NULL pointer dereference during unbind race Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") introduced two stages of synchronization waits totaling 1500ms in uvc_function_unbind() to prevent several types of kernel panics. However, this timing-based approach is insufficient during power management (PM) transitions. When the PM subsystem starts freezing user space processes, the wait_event_interruptible_timeout() is aborted early, which allows the unbind thread to proceed and nullify the gadget pointer (cdev->gadget = NULL): [ 814.123447][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind() [ 814.178583][ T3173] PM: suspend entry (deep) [ 814.192487][ T3173] Freezing user space processes [ 814.197668][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release When the PM subsystem resumes or aborts the suspend and tasks are restarted, the V4L2 release path is executed and attempts to access the already nullified gadget pointer, triggering a kernel panic: [ 814.292597][ C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake [ 814.386727][ T3173] Restarting tasks ... [ 814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030 [ 814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4 [ 814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94 [ 814.404078][ T4558] Call trace: [ 814.404080][ T4558] usb_gadget_deactivate+0x14/0xf4 [ 814.404083][ T4558] usb_function_deactivate+0x54/0x94 [ 814.404087][ T4558] uvc_function_disconnect+0x1c/0x5c [ 814.404092][ T4558] uvc_v4l2_release+0x44/0xac [ 814.404095][ T4558] v4l2_release+0xcc/0x130 Address the race condition and NULL pointer dereference by: 1. State Synchronization (flag + mutex) Introduce a 'func_unbound' flag in struct uvc_device. This allows uvc_function_disconnect() to safely skip accessing the nullified cdev->gadget pointer. As suggested by Alan Stern, this flag is protected by a new mutex (uvc->lock) to ensure proper memory ordering and prevent instruction reordering or speculative loads. This mutex is also used to protect 'func_connected' for consistent state management. 2. Explicit Synchronization (completion) Use a completion to synchronize uvc_function_unbind() with the uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by ensuring struct uvc_device is freed after all video device resources are released. Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") Cc: stable Suggested-by: Alan Stern Suggested-by: Greg Kroah-Hartman Signed-off-by: Jimmy Hu Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 39 ++++++++++++++++++++++++-- drivers/usb/gadget/function/uvc.h | 3 ++ drivers/usb/gadget/function/uvc_v4l2.c | 5 +++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 494fdbc4e85b5c..8d404d88391c51 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -413,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc) { int ret; + guard(mutex)(&uvc->lock); + if (uvc->func_unbound) { + dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n"); + return; + } + if ((ret = usb_function_deactivate(&uvc->func)) < 0) uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret); } @@ -431,6 +437,15 @@ static ssize_t function_name_show(struct device *dev, static DEVICE_ATTR_RO(function_name); +static void uvc_vdev_release(struct video_device *vdev) +{ + struct uvc_device *uvc = video_get_drvdata(vdev); + + /* Signal uvc_function_unbind() that the video device has been released */ + if (uvc->vdev_release_done) + complete(uvc->vdev_release_done); +} + static int uvc_register_video(struct uvc_device *uvc) { @@ -443,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc) uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.fops = &uvc_v4l2_fops; uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops; - uvc->vdev.release = video_device_release_empty; + uvc->vdev.release = uvc_vdev_release; uvc->vdev.vfl_dir = VFL_DIR_TX; uvc->vdev.lock = &uvc->video.mutex; uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -659,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) int ret = -EINVAL; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) + uvc->func_unbound = false; opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ @@ -988,12 +1005,19 @@ static void uvc_free(struct usb_function *f) static void uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) { + DECLARE_COMPLETION_ONSTACK(vdev_release_done); struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); struct uvc_video *video = &uvc->video; long wait_ret = 1; + bool connected; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) { + uvc->func_unbound = true; + uvc->vdev_release_done = &vdev_release_done; + connected = uvc->func_connected; + } kthread_cancel_work_sync(&video->hw_submit); @@ -1006,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c, * though the video device removal uevent. Allow some time for the * application to close out before things get deleted. */ - if (uvc->func_connected) { + if (connected) { uvcg_dbg(f, "waiting for clean disconnect\n"); wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue, uvc->func_connected == false, msecs_to_jiffies(500)); @@ -1017,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c, video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); - if (uvc->func_connected) { + scoped_guard(mutex, &uvc->lock) + connected = uvc->func_connected; + + if (connected) { /* * Wait for the release to occur to ensure there are no longer any * pending operations that may cause panics when resources are cleaned @@ -1029,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret); } + /* Wait for the video device to be released */ + wait_for_completion(&vdev_release_done); + uvc->vdev_release_done = NULL; + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -1047,6 +1078,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); mutex_init(&uvc->video.mutex); + mutex_init(&uvc->lock); + uvc->func_unbound = true; uvc->state = UVC_STATE_DISCONNECTED; init_waitqueue_head(&uvc->func_connected_queue); opts = fi_to_f_uvc_opts(fi); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 676419a049762f..7abfdd5e1eefb6 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -155,6 +155,9 @@ struct uvc_device { enum uvc_state state; struct usb_function func; struct uvc_video video; + struct completion *vdev_release_done; + struct mutex lock; /* protects func_unbound and func_connected */ + bool func_unbound; bool func_connected; wait_queue_head_t func_connected_queue; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ed48d38498fbb9..514e5930b9ca9d 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; + guard(mutex)(&uvc->lock); + if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) return -EBUSY; @@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) uvc_function_disconnect(uvc); uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); - uvc->func_connected = false; + scoped_guard(mutex, &uvc->lock) + uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); } From 9bb4b5ed7f8c4f95cc556bdf042b0ba2fa13557a Mon Sep 17 00:00:00 2001 From: Juno Choi Date: Tue, 24 Mar 2026 10:49:10 +0900 Subject: [PATCH 211/700] usb: dwc2: gadget: Fix spin_lock/unlock mismatch in dwc2_hsotg_udc_stop() dwc2_gadget_exit_clock_gating() internally calls call_gadget() macro, which expects hsotg->lock to be held since it does spin_unlock/spin_lock around the gadget driver callback invocation. However, dwc2_hsotg_udc_stop() calls dwc2_gadget_exit_clock_gating() without holding the lock. This leads to: - spin_unlock on a lock that is not held (undefined behavior) - The lock remaining held after dwc2_gadget_exit_clock_gating() returns, causing a deadlock when spin_lock_irqsave() is called later in the same function. Fix this by acquiring hsotg->lock before calling dwc2_gadget_exit_clock_gating() and releasing it afterwards, which satisfies the locking requirement of the call_gadget() macro. Fixes: af076a41f8a2 ("usb: dwc2: also exit clock_gating when stopping udc while suspended") Cc: stable Signed-off-by: Juno Choi Link: https://patch.msgid.link/20260324014910.2798425-1-juno.choi@lge.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index d216e26c787b0f..c8b02c27d27d4f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4607,7 +4607,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) /* Exit clock gating when driver is stopped. */ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && !hsotg->params.no_clock_gating) { + spin_lock_irqsave(&hsotg->lock, flags); dwc2_gadget_exit_clock_gating(hsotg, 0); + spin_unlock_irqrestore(&hsotg->lock, flags); } /* all endpoints should be shutdown */ From b65d4ca1d1059ace899170d1dfd02639ce971d2d Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Tue, 24 Mar 2026 10:29:03 +0000 Subject: [PATCH 212/700] usb: typec: Remove alt->adev.dev.class assignment The typec plug alternate mode is already registered as part of the bus. When both class and bus are set for a device, device_add() attempts to create the "subsystem" symlink in the device's sysfs directory twice, once for the bus and once for the class. This results in a duplicate filename error during registration, causing the alternate mode registration to fail with warnings: cannot create duplicate filename '/devices/pci0000:00/0000:00:1f.0/ PNP0C09:00/GOOG0004:00/cros-ec-dev.1.auto/cros_ec_ucsi.3.auto/typec/ port1/port1-cable/port1-plug0/port1-plug0.0/subsystem' typec port0-plug0: failed to register alternate mode (-17) cros_ec_ucsi.3.auto: failed to registers svid 0x8087 mode 1 Cc: stable Fixes: 67ab45426215 ("usb: typec: Set the bus also for the port and plug altmodes") Tested-by: Madhu M Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260324102903.1416210-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 83143090947191..0977581ad1b6e9 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -686,10 +686,6 @@ typec_register_altmode(struct device *parent, alt->adev.dev.bus = &typec_bus; - /* Plug alt modes need a class to generate udev events. */ - if (is_typec_plug(parent)) - alt->adev.dev.class = &typec_class; - ret = device_register(&alt->adev.dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", From 3b8ae9817686efb3ea789ca9d4efdff2ce9c1c04 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Tue, 24 Mar 2026 10:30:12 +0000 Subject: [PATCH 213/700] usb: typec: thunderbolt: Set enter_vdo during initialization In the current implementation, if a cable's alternate mode enter operation is not supported, the tbt->plug[TYPEC_PLUG_SOP_P] pointer is cleared by the time tbt_enter_mode() is called. This prevents the driver from identifying the cable's VDO. As a result, the Thunderbolt connection falls back to the default TBT_CABLE_USB3_PASSIVE speed, even if the cable supports higher speeds. To ensure the correct VDO value is used during mode entry, calculate and store the enter_vdo earlier during the initialization phase in tbt_ready(). Cc: stable Fixes: 100e25738659 ("usb: typec: Add driver for Thunderbolt 3 Alternate Mode") Tested-by: Madhu M Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260324103012.1417616-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/thunderbolt.c | 44 ++++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index c4c5da6154da95..32250b94262a90 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -39,28 +39,7 @@ static bool tbt_ready(struct typec_altmode *alt); static int tbt_enter_mode(struct tbt_altmode *tbt) { - struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P]; - u32 vdo; - - vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); - vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; - vdo |= TBT_MODE; - - if (plug) { - if (typec_cable_is_active(tbt->cable)) - vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; - - vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo)); - vdo |= plug->vdo & TBT_CABLE_ROUNDED; - vdo |= plug->vdo & TBT_CABLE_OPTICAL; - vdo |= plug->vdo & TBT_CABLE_RETIMER; - vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING; - } else { - vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE); - } - - tbt->enter_vdo = vdo; - return typec_altmode_enter(tbt->alt, &vdo); + return typec_altmode_enter(tbt->alt, &tbt->enter_vdo); } static void tbt_altmode_work(struct work_struct *work) @@ -337,6 +316,7 @@ static bool tbt_ready(struct typec_altmode *alt) { struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); struct typec_altmode *plug; + u32 vdo; if (tbt->cable) return true; @@ -364,6 +344,26 @@ static bool tbt_ready(struct typec_altmode *alt) tbt->plug[i] = plug; } + vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); + vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; + vdo |= TBT_MODE; + plug = tbt->plug[TYPEC_PLUG_SOP_P]; + + if (plug) { + if (typec_cable_is_active(tbt->cable)) + vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; + + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo)); + vdo |= plug->vdo & TBT_CABLE_ROUNDED; + vdo |= plug->vdo & TBT_CABLE_OPTICAL; + vdo |= plug->vdo & TBT_CABLE_RETIMER; + vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING; + } else { + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE); + } + + tbt->enter_vdo = vdo; + return true; } From 269c26464dcf8b54b0dd9c333721c30ee44ae297 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 30 Mar 2026 14:35:18 +0800 Subject: [PATCH 214/700] dt-bindings: connector: add pd-disable dependency When Power Delivery is not supported, the source is unable to obtain the current capability from the Source PDO. As a result, typec-power-opmode needs to be added to advertise such capability. Acked-by: Conor Dooley Cc: stable Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260330063518.719345-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/connector/usb-connector.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 11e40d225b9f3a..d97b29e49bf55b 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -301,6 +301,7 @@ properties: maxItems: 4 dependencies: + pd-disable: [typec-power-opmode] sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ] From caa27923aacd8a5869207842f2ab1657c6c0c7bc Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:44 +0800 Subject: [PATCH 215/700] usb: gadget: f_subset: Fix unbalanced refcnt in geth_free geth_alloc() increments the reference count, but geth_free() fails to decrement it. This prevents the configuration of attributes via configfs after unlinking the function. Decrement the reference count in geth_free() to ensure proper cleanup. Fixes: 02832e56f88a ("usb: gadget: f_subset: add configfs support") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-1-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_subset.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 076072386e5e0d..74dc6da5c7674a 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -6,6 +6,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include #include #include #include @@ -449,8 +450,13 @@ static struct usb_function_instance *geth_alloc_inst(void) static void geth_free(struct usb_function *f) { struct f_gether *eth; + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); eth = func_to_geth(f); + scoped_guard(mutex, &opts->lock) + opts->refcnt--; kfree(eth); } From 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:45 +0800 Subject: [PATCH 216/700] usb: gadget: f_rndis: Protect RNDIS options with mutex The class/subclass/protocol options are suspectible to race conditions as they can be accessed concurrently through configfs. Use existing mutex to protect these options. This issue was identified during code inspection. Fixes: 73517cf49bd4 ("usb: gadget: add RNDIS configfs options for class/subclass/protocol") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-2-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_rndis.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 8b11d8d6d89cec..521b4619d6befb 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -11,6 +11,7 @@ /* #define VERBOSE_DEBUG */ +#include #include #include #include @@ -678,9 +679,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return -ENOMEM; } - rndis_iad_descriptor.bFunctionClass = rndis_opts->class; - rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; - rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + scoped_guard(mutex, &rndis_opts->lock) { + rndis_iad_descriptor.bFunctionClass = rndis_opts->class; + rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; + rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + } /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() From 57f531df3618b26df053798a96aa5ade44e00e1a Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:46 +0800 Subject: [PATCH 217/700] usb: gadget: u_ncm: Add kernel-doc comments for struct f_ncm_opts Provide kernel-doc descriptions for the fields in struct f_ncm_opts to improve code readability and maintainability. Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-3-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_ncm.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index b1f3db8b68c15e..ce2f6358688af2 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -15,6 +15,20 @@ #include +/** + * struct f_ncm_opts - NCM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the NCM function. + * @bind_count: Tracks the number of configurations the NCM function is + * bound to, preventing double-registration of the @net device. + * @ncm_interf_group: ConfigFS group for NCM interface. + * @ncm_os_desc: USB OS descriptor for NCM. + * @ncm_ext_compat_id: Extended compatibility ID. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + * @max_segment_size: Maximum segment size. + */ struct f_ncm_opts { struct usb_function_instance func_inst; struct net_device *net; @@ -23,12 +37,7 @@ struct f_ncm_opts { struct config_group *ncm_interf_group; struct usb_os_desc ncm_os_desc; char ncm_ext_compat_id[16]; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + struct mutex lock; int refcnt; From b2cc4fae67a51f60d81d6af2678696accb07c656 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:47 +0800 Subject: [PATCH 218/700] usb: gadget: f_ecm: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: fee562a6450b ("usb: gadget: f_ecm: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-4-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 37 +++++++++++++++++++---------- drivers/usb/gadget/function/u_ecm.h | 21 +++++++++++----- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index e0c02121374e0d..e495bac4efeb5a 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -681,6 +681,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ecm_opts *ecm_opts; + struct net_device *net __free(detach_gadget) = NULL; struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_ecm(cdev->gadget)) @@ -688,18 +689,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); - mutex_lock(&ecm_opts->lock); - - gether_set_gadget(ecm_opts->net, cdev->gadget); - - if (!ecm_opts->bound) { - status = gether_register_netdev(ecm_opts->net); - ecm_opts->bound = true; - } - - mutex_unlock(&ecm_opts->lock); - if (status) - return status; + scoped_guard(mutex, &ecm_opts->lock) + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) { + if (!device_is_registered(&ecm_opts->net->dev)) { + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + } else + status = gether_attach_gadget(ecm_opts->net, cdev->gadget); + + if (status) + return status; + net = ecm_opts->net; + } ecm_string_defs[1].s = ecm->ethaddr; @@ -790,6 +791,9 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->notify_req = no_free_ptr(request); + ecm_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); @@ -836,7 +840,7 @@ static void ecm_free_inst(struct usb_function_instance *f) struct f_ecm_opts *opts; opts = container_of(f, struct f_ecm_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -906,9 +910,12 @@ static void ecm_free(struct usb_function *f) static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ecm *ecm = func_to_ecm(f); + struct f_ecm_opts *ecm_opts; DBG(c->cdev, "ecm unbind\n"); + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + usb_free_all_descriptors(f); if (atomic_read(&ecm->notify_count)) { @@ -918,6 +925,10 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); + + ecm_opts->bind_count--; + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) + gether_detach_gadget(ecm_opts->net); } static struct usb_function *ecm_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 77cfb89932bea7..7f666b9dea0265 100644 --- a/drivers/usb/gadget/function/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h @@ -15,17 +15,26 @@ #include +/** + * struct f_ecm_opts - ECM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the ECM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the ECM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the ECM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_ecm_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From d9270c9a8118c1535409db926ac1e2545dc97b81 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:48 +0800 Subject: [PATCH 219/700] usb: gadget: f_eem: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: b29002a15794 ("usb: gadget: f_eem: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-5-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_eem.c | 59 +++++++++++++++-------------- drivers/usb/gadget/function/u_eem.h | 21 +++++++--- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 0142a0e487ee32..ac37d7c1d168bd 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -7,6 +7,7 @@ * Copyright (C) 2009 EF Johnson Technologies */ +#include #include #include #include @@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_eem_opts *eem_opts; + struct net_device *net __free(detach_gadget) = NULL; eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to eem_opts->bound access - */ - if (!eem_opts->bound) { - mutex_lock(&eem_opts->lock); - gether_set_gadget(eem_opts->net, cdev->gadget); - status = gether_register_netdev(eem_opts->net); - mutex_unlock(&eem_opts->lock); - if (status) - return status; - eem_opts->bound = true; - } + + scoped_guard(mutex, &eem_opts->lock) + if (eem_opts->bind_count == 0 && !eem_opts->bound) { + if (!device_is_registered(&eem_opts->net->dev)) { + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + } else + status = gether_attach_gadget(eem_opts->net, cdev->gadget); + + if (status) + return status; + net = eem_opts->net; + } us = usb_gstrings_attach(cdev, eem_strings, ARRAY_SIZE(eem_string_defs)); @@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; eem->ctrl_id = status; eem_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, eem_ss_function, eem_ss_function); if (status) - goto fail; + return status; + + eem_opts->bind_count++; + retain_and_null_ptr(net); DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n", eem->port.in_ep->name, eem->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) @@ -597,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f) struct f_eem_opts *opts; opts = container_of(f, struct f_eem_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -640,9 +635,17 @@ static void eem_free(struct usb_function *f) static void eem_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_eem_opts *opts; + DBG(c->cdev, "eem unbind\n"); + opts = container_of(f->fi, struct f_eem_opts, func_inst); + usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *eem_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h index 3bd85dfcd71c81..78ef5581521972 100644 --- a/drivers/usb/gadget/function/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h @@ -15,17 +15,26 @@ #include +/** + * struct f_eem_opts - EEM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the EEM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the EEM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the EEM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_eem_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From 06524cd1c9011bee141a87e43ab878641ed3652b Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:49 +0800 Subject: [PATCH 220/700] usb: gadget: f_subset: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: 8cedba7c73af ("usb: gadget: f_subset: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-6-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_subset.c | 57 +++++++++++++------------- drivers/usb/gadget/function/u_gether.h | 22 ++++++---- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 74dc6da5c7674a..6e3265b8a3a035 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -299,25 +299,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_gether_opts *gether_opts; + struct net_device *net __free(detach_gadget) = NULL; gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to gether_opts->bound access - */ - if (!gether_opts->bound) { - mutex_lock(&gether_opts->lock); - gether_set_gadget(gether_opts->net, cdev->gadget); - status = gether_register_netdev(gether_opts->net); - mutex_unlock(&gether_opts->lock); - if (status) - return status; - gether_opts->bound = true; - } + scoped_guard(mutex, &gether_opts->lock) + if (gether_opts->bind_count == 0 && !gether_opts->bound) { + if (!device_is_registered(&gether_opts->net->dev)) { + gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); + } else + status = gether_attach_gadget(gether_opts->net, cdev->gadget); + + if (status) + return status; + net = gether_opts->net; + } us = usb_gstrings_attach(cdev, geth_strings, ARRAY_SIZE(geth_string_defs)); @@ -330,20 +327,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; subset_data_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -361,21 +356,19 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, ss_eth_function, ss_eth_function); if (status) - goto fail; + return status; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). */ + gether_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Subset: IN/%s OUT/%s\n", geth->port.in_ep->name, geth->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) @@ -418,7 +411,7 @@ static void geth_free_inst(struct usb_function_instance *f) struct f_gether_opts *opts; opts = container_of(f, struct f_gether_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -462,8 +455,16 @@ static void geth_free(struct usb_function *f) static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); + geth_string_defs[0].id = 0; usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *geth_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h index 2f7a373ed4496a..e7b6b51f69c12a 100644 --- a/drivers/usb/gadget/function/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h @@ -15,17 +15,25 @@ #include +/** + * struct f_gether_opts - subset function options + * @func_inst: USB function instance. + * @net: The net_device associated with the subset function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the subset function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the subset function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_gether_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + int bind_count; struct mutex lock; int refcnt; }; From e367599529dc42578545a7f85fde517b35b3cda7 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 20 Mar 2026 16:54:50 +0800 Subject: [PATCH 221/700] usb: gadget: f_rndis: Fix net_device lifecycle with device_move The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the borrowed_net flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: f466c6353819 ("usb: gadget: f_rndis: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-7-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_rndis.c | 42 ++++++++++++++++----------- drivers/usb/gadget/function/u_rndis.h | 31 +++++++++++++++----- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 521b4619d6befb..7de1c5f8e32605 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -666,6 +666,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) struct f_rndis_opts *rndis_opts; struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct net_device *net __free(detach_gadget) = NULL; struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_rndis(c)) @@ -683,21 +684,18 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_iad_descriptor.bFunctionClass = rndis_opts->class; rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; - } - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to rndis_opts->bound access - */ - if (!rndis_opts->bound) { - gether_set_gadget(rndis_opts->net, cdev->gadget); - status = gether_register_netdev(rndis_opts->net); - if (status) - return status; - rndis_opts->bound = true; + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) { + if (!device_is_registered(&rndis_opts->net->dev)) { + gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); + } else + status = gether_attach_gadget(rndis_opts->net, cdev->gadget); + + if (status) + return status; + net = rndis_opts->net; + } } us = usb_gstrings_attach(cdev, rndis_strings, @@ -796,6 +794,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) } rndis->notify_req = no_free_ptr(request); + rndis_opts->bind_count++; + retain_and_null_ptr(net); + /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). @@ -812,11 +813,11 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) struct f_rndis_opts *opts; opts = container_of(f, struct f_rndis_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); - opts->borrowed_net = opts->bound = true; + opts->borrowed_net = true; opts->net = net; } EXPORT_SYMBOL_GPL(rndis_borrow_net); @@ -874,7 +875,7 @@ static void rndis_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_rndis_opts, func_inst); if (!opts->borrowed_net) { - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -943,6 +944,9 @@ static void rndis_free(struct usb_function *f) static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); + struct f_rndis_opts *rndis_opts; + + rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(f->os_desc_table); f->os_desc_n = 0; @@ -950,6 +954,10 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); + + rndis_opts->bind_count--; + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) + gether_detach_gadget(rndis_opts->net); } static struct usb_function *rndis_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index a8c409b2f52f9e..4e64619714dc2d 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -15,12 +15,34 @@ #include +/** + * struct f_rndis_opts - RNDIS function options + * @func_inst: USB function instance. + * @vendor_id: Vendor ID. + * @manufacturer: Manufacturer string. + * @net: The net_device associated with the RNDIS function. + * @bind_count: Tracks the number of configurations the RNDIS function is + * bound to, preventing double-registration of the @net device. + * @borrowed_net: True if the net_device is shared and pre-registered during + * the legacy composite driver's bind phase (e.g., multi.c). + * If false, the RNDIS function will register the net_device + * during its own bind phase. + * @rndis_interf_group: ConfigFS group for RNDIS interface. + * @rndis_os_desc: USB OS descriptor for RNDIS. + * @rndis_ext_compat_id: Extended compatibility ID. + * @class: USB class. + * @subclass: USB subclass. + * @protocol: USB protocol. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_rndis_opts { struct usb_function_instance func_inst; u32 vendor_id; const char *manufacturer; struct net_device *net; - bool bound; + int bind_count; bool borrowed_net; struct config_group *rndis_interf_group; @@ -30,13 +52,6 @@ struct f_rndis_opts { u8 class; u8 subclass; u8 protocol; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; From 40014493cece72a0be5672cd86763e53fb3ec613 Mon Sep 17 00:00:00 2001 From: Liav Mordouch Date: Fri, 27 Mar 2026 20:02:04 +0300 Subject: [PATCH 222/700] vt: discard stale unicode buffer on alt screen exit after resize When enter_alt_screen() saves vc_uni_lines into vc_saved_uni_lines and sets vc_uni_lines to NULL, a subsequent console resize via vc_do_resize() skips reallocating the unicode buffer because vc_uni_lines is NULL. However, vc_saved_uni_lines still points to the old buffer allocated for the original dimensions. When leave_alt_screen() later restores vc_saved_uni_lines, the buffer dimensions no longer match vc_rows/vc_cols. Any operation that iterates over the unicode buffer using the current dimensions (e.g. csi_J clearing the screen) will access memory out of bounds, causing a kernel oops: BUG: unable to handle page fault for address: 0x0000002000000020 RIP: 0010:csi_J+0x133/0x2d0 The faulting address 0x0000002000000020 is two adjacent u32 space characters (0x20) interpreted as a pointer, read from the row data area past the end of the 25-entry pointer array in a buffer allocated for 80x25 but accessed with 240x67 dimensions. Fix this by checking whether the console dimensions changed while in the alternate screen. If they did, free the stale saved buffer instead of restoring it. The unicode screen will be lazily rebuilt via vc_uniscr_check() when next needed. Fixes: 5eb608319bb5 ("vt: save/restore unicode screen buffer for alternate screen") Cc: stable Tested-by: Liav Mordouch Signed-off-by: Liav Mordouch Reviewed-by: Nicolas Pitre Link: https://patch.msgid.link/20260327170204.29706-1-liavmordouch@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e2df99e3d45809..99f15b3e954478 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1901,6 +1901,7 @@ static void leave_alt_screen(struct vc_data *vc) unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows); unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols); u16 *src, *dest; + bool uni_lines_stale; if (vc->vc_saved_screen == NULL) return; /* Not inside an alt-screen */ @@ -1909,7 +1910,18 @@ static void leave_alt_screen(struct vc_data *vc) dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols; memcpy(dest, src, 2 * cols); } - vc_uniscr_set(vc, vc->vc_saved_uni_lines); + /* + * If the console was resized while in the alternate screen, + * vc_saved_uni_lines was allocated for the old dimensions. + * Restoring it would cause out-of-bounds accesses. Discard it + * and let the unicode screen be lazily rebuilt. + */ + uni_lines_stale = vc->vc_saved_rows != vc->vc_rows || + vc->vc_saved_cols != vc->vc_cols; + if (uni_lines_stale) + vc_uniscr_free(vc->vc_saved_uni_lines); + else + vc_uniscr_set(vc, vc->vc_saved_uni_lines); vc->vc_saved_uni_lines = NULL; restore_cur(vc); /* Update the entire screen */ From 3ddbea7542ae529c1a88ef9a8b1ce169126211f6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 27 Mar 2026 23:09:47 -0400 Subject: [PATCH 223/700] vt: resize saved unicode buffer on alt screen exit after resize Instead of discarding the saved unicode buffer when the console was resized while in the alternate screen, resize it to the current dimensions using vc_uniscr_copy_area() to preserve its content. This properly restores the unicode screen on alt screen exit rather than lazily rebuilding it from a lossy reverse glyph translation. On allocation failure the stale buffer is freed and vc_uni_lines is set to NULL so it gets lazily rebuilt via vc_uniscr_check() when next needed. Fixes: 40014493cece ("vt: discard stale unicode buffer on alt screen exit after resize") Cc: stable Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/3nsr334n-079q-125n-7807-n4nq818758ns@syhkavp.arg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 99f15b3e954478..b4b19157f05c2b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1901,7 +1901,6 @@ static void leave_alt_screen(struct vc_data *vc) unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows); unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols); u16 *src, *dest; - bool uni_lines_stale; if (vc->vc_saved_screen == NULL) return; /* Not inside an alt-screen */ @@ -1912,16 +1911,23 @@ static void leave_alt_screen(struct vc_data *vc) } /* * If the console was resized while in the alternate screen, - * vc_saved_uni_lines was allocated for the old dimensions. - * Restoring it would cause out-of-bounds accesses. Discard it - * and let the unicode screen be lazily rebuilt. + * resize the saved unicode buffer to the current dimensions. + * On allocation failure new_uniscr is NULL, causing the old + * buffer to be freed and vc_uni_lines to be lazily rebuilt + * via vc_uniscr_check() when next needed. */ - uni_lines_stale = vc->vc_saved_rows != vc->vc_rows || - vc->vc_saved_cols != vc->vc_cols; - if (uni_lines_stale) + if (vc->vc_saved_uni_lines && + (vc->vc_saved_rows != vc->vc_rows || + vc->vc_saved_cols != vc->vc_cols)) { + u32 **new_uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); + + if (new_uniscr) + vc_uniscr_copy_area(new_uniscr, vc->vc_cols, vc->vc_rows, + vc->vc_saved_uni_lines, cols, 0, rows); vc_uniscr_free(vc->vc_saved_uni_lines); - else - vc_uniscr_set(vc, vc->vc_saved_uni_lines); + vc->vc_saved_uni_lines = new_uniscr; + } + vc_uniscr_set(vc, vc->vc_saved_uni_lines); vc->vc_saved_uni_lines = NULL; restore_cur(vc); /* Update the entire screen */ From 2e8b1a1d12ae3338efeb1c3de3eb4e9324b87a28 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 30 Mar 2026 11:12:07 +0200 Subject: [PATCH 224/700] rtla: Fix build without libbpf header rtla supports building without libbpf. However, BPF actions patchset [1] adds an include of bpf/libbpf.h into timerlat_bpf.h, which breaks build on systems that don't have libbpf headers installed. This is a leftover from a draft version of the patchset where timerlat_bpf_set_action() (which takes a struct bpf_program * argument) was defined in the header. timerlat_bpf.c already includes bpf/libbpf.h via timerlat.skel.h when libbpf is present. Remove the redundant include to fix build on systems without libbpf headers. [1] https://lore.kernel.org/linux-trace-kernel/20251126144205.331954-1-tglozar@redhat.com/T/ Cc: John Kacur Cc: Luis Goncalves Cc: Crystal Wood Cc: Costa Shulyupin Link: https://patch.msgid.link/20260330091207.16184-1-tglozar@redhat.com Reported-by: Steven Rostedt (Google) Closes: https://lore.kernel.org/linux-trace-kernel/20260329122202.65a8b575@robin/ Fixes: 8cd0f08ac72e ("rtla/timerlat: Support tail call from BPF program") Signed-off-by: Tomas Glozar Reviewed-by: Wander Lairson Costa Signed-off-by: Steven Rostedt (Google) --- tools/tracing/rtla/src/timerlat_bpf.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h index 169abeaf436399..f7c5675737fe4c 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.h +++ b/tools/tracing/rtla/src/timerlat_bpf.h @@ -12,7 +12,6 @@ enum summary_field { }; #ifndef __bpf__ -#include #ifdef HAVE_BPF_SKEL int timerlat_bpf_init(struct timerlat_params *params); int timerlat_bpf_attach(void); From ca34ee6d0307a0b4e52c870dfc1bb8a3c3eb956e Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Mon, 30 Mar 2026 15:56:40 +0000 Subject: [PATCH 225/700] hwmon: (tps53679) Fix device ID comparison and printing in tps53676_identify() tps53676_identify() uses strncmp() to compare the device ID buffer against a byte sequence containing embedded non-printable bytes (\x53\x67\x60). strncmp() is semantically wrong for binary data comparison; use memcmp() instead. Additionally, the buffer from i2c_smbus_read_block_data() is not NUL-terminated, so printing it with "%s" in the error path is undefined behavior and may read past the buffer. Use "%*ph" to hex-dump the actual bytes returned. Per the datasheet, the expected device ID is the 6-byte sequence 54 49 53 67 60 00 ("TI\x53\x67\x60\x00"), so compare all 6 bytes including the trailing NUL. Fixes: cb3d37b59012 ("hwmon: (pmbus/tps53679) Add support for TI TPS53676") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260330155618.77403-1-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/tps53679.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 3bca543817a605..249974c13aa399 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -175,8 +175,8 @@ static int tps53676_identify(struct i2c_client *client, ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); if (ret < 0) return ret; - if (strncmp("TI\x53\x67\x60", buf, 5)) { - dev_err(&client->dev, "Unexpected device ID: %s\n", buf); + if (ret != 6 || memcmp(buf, "TI\x53\x67\x60\x00", 6)) { + dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf); return -ENODEV; } From b0dc7e7c56573e7a52080f25f3179a45f3dd7e6f Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 22 Mar 2026 22:28:45 +0800 Subject: [PATCH 226/700] spi: amlogic: spifc-a4: unregister ECC engine on probe failure and remove() callback aml_sfc_probe() registers the on-host NAND ECC engine, but teardown was missing from both probe unwind and remove-time cleanup. Add a devm cleanup action after successful registration so nand_ecc_unregister_on_host_hw_engine() runs automatically on probe failures and during device removal. Fixes: 4670db6f32e9 ("spi: amlogic: add driver for Amlogic SPI Flash Controller") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260322-spifc-a4-v1-1-2dc5ebcbe0a9@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spifc-a4.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/spi/spi-amlogic-spifc-a4.c b/drivers/spi/spi-amlogic-spifc-a4.c index 1aabafa36e48ed..16346f50c7d999 100644 --- a/drivers/spi/spi-amlogic-spifc-a4.c +++ b/drivers/spi/spi-amlogic-spifc-a4.c @@ -1066,6 +1066,13 @@ static const struct nand_ecc_engine_ops aml_sfc_ecc_engine_ops = { .finish_io_req = aml_sfc_ecc_finish_io_req, }; +static void aml_sfc_unregister_ecc_engine(void *data) +{ + struct nand_ecc_engine *eng = data; + + nand_ecc_unregister_on_host_hw_engine(eng); +} + static int aml_sfc_clk_init(struct aml_sfc *sfc) { sfc->gate_clk = devm_clk_get_enabled(sfc->dev, "gate"); @@ -1149,6 +1156,11 @@ static int aml_sfc_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "failed to register Aml host ecc engine.\n"); + ret = devm_add_action_or_reset(dev, aml_sfc_unregister_ecc_engine, + &sfc->ecc_eng); + if (ret) + return dev_err_probe(dev, ret, "failed to add ECC unregister action\n"); + ret = of_property_read_u32(np, "amlogic,rx-adj", &val); if (!ret) sfc->rx_adj = val; From 415cb193bb9736f0e830286c72a6fa8eb2a9cc5c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Mar 2026 14:18:55 -1000 Subject: [PATCH 227/700] sched_ext: Fix SCX_KICK_WAIT deadlock by deferring wait to balance callback SCX_KICK_WAIT busy-waits in kick_cpus_irq_workfn() using smp_cond_load_acquire() until the target CPU's kick_sync advances. Because the irq_work runs in hardirq context, the waiting CPU cannot reschedule and its own kick_sync never advances. If multiple CPUs form a wait cycle, all CPUs deadlock. Replace the busy-wait in kick_cpus_irq_workfn() with resched_curr() to force the CPU through do_pick_task_scx(), which queues a balance callback to perform the wait. The balance callback drops the rq lock and enables IRQs following the sched_core_balance() pattern, so the CPU can process IPIs while waiting. The local CPU's kick_sync is advanced on entry to do_pick_task_scx() and continuously during the wait, ensuring any CPU that starts waiting for us sees the advancement and cannot form cyclic dependencies. Fixes: 90e55164dad4 ("sched_ext: Implement SCX_KICK_WAIT") Cc: stable@vger.kernel.org # v6.12+ Reported-by: Christian Loehle Link: https://lore.kernel.org/r/20260316100249.1651641-1-christian.loehle@arm.com Signed-off-by: Tejun Heo Tested-by: Christian Loehle --- kernel/sched/ext.c | 95 ++++++++++++++++++++++++++++++++------------ kernel/sched/sched.h | 3 ++ 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 26a6ac2f882677..d5bdcdb3f70041 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2404,7 +2404,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, { struct scx_sched *sch = scx_root; - /* see kick_cpus_irq_workfn() */ + /* see kick_sync_wait_bal_cb() */ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); update_curr_scx(rq); @@ -2447,6 +2447,48 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, switch_class(rq, next); } +static void kick_sync_wait_bal_cb(struct rq *rq) +{ + struct scx_kick_syncs __rcu *ks = __this_cpu_read(scx_kick_syncs); + unsigned long *ksyncs = rcu_dereference_sched(ks)->syncs; + bool waited; + s32 cpu; + + /* + * Drop rq lock and enable IRQs while waiting. IRQs must be enabled + * — a target CPU may be waiting for us to process an IPI (e.g. TLB + * flush) while we wait for its kick_sync to advance. + * + * Also, keep advancing our own kick_sync so that new kick_sync waits + * targeting us, which can start after we drop the lock, cannot form + * cyclic dependencies. + */ +retry: + waited = false; + for_each_cpu(cpu, rq->scx.cpus_to_sync) { + /* + * smp_load_acquire() pairs with smp_store_release() on + * kick_sync updates on the target CPUs. + */ + if (cpu == cpu_of(rq) || + smp_load_acquire(&cpu_rq(cpu)->scx.kick_sync) != ksyncs[cpu]) { + cpumask_clear_cpu(cpu, rq->scx.cpus_to_sync); + continue; + } + + raw_spin_rq_unlock_irq(rq); + while (READ_ONCE(cpu_rq(cpu)->scx.kick_sync) == ksyncs[cpu]) { + smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); + cpu_relax(); + } + raw_spin_rq_lock_irq(rq); + waited = true; + } + + if (waited) + goto retry; +} + static struct task_struct *first_local_task(struct rq *rq) { return list_first_entry_or_null(&rq->scx.local_dsq.list, @@ -2460,7 +2502,7 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) bool keep_prev; struct task_struct *p; - /* see kick_cpus_irq_workfn() */ + /* see kick_sync_wait_bal_cb() */ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); rq_modified_begin(rq, &ext_sched_class); @@ -2470,6 +2512,17 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) rq_repin_lock(rq, rf); maybe_queue_balance_callback(rq); + /* + * Defer to a balance callback which can drop rq lock and enable + * IRQs. Waiting directly in the pick path would deadlock against + * CPUs sending us IPIs (e.g. TLB flushes) while we wait for them. + */ + if (unlikely(rq->scx.kick_sync_pending)) { + rq->scx.kick_sync_pending = false; + queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb, + kick_sync_wait_bal_cb); + } + /* * If any higher-priority sched class enqueued a runnable task on * this rq during balance_one(), abort and return RETRY_TASK, so @@ -4713,6 +4766,9 @@ static void scx_dump_state(struct scx_exit_info *ei, size_t dump_len) if (!cpumask_empty(rq->scx.cpus_to_wait)) dump_line(&ns, " cpus_to_wait : %*pb", cpumask_pr_args(rq->scx.cpus_to_wait)); + if (!cpumask_empty(rq->scx.cpus_to_sync)) + dump_line(&ns, " cpus_to_sync : %*pb", + cpumask_pr_args(rq->scx.cpus_to_sync)); used = seq_buf_used(&ns); if (SCX_HAS_OP(sch, dump_cpu)) { @@ -5610,11 +5666,11 @@ static bool kick_one_cpu(s32 cpu, struct rq *this_rq, unsigned long *ksyncs) if (cpumask_test_cpu(cpu, this_scx->cpus_to_wait)) { if (cur_class == &ext_sched_class) { + cpumask_set_cpu(cpu, this_scx->cpus_to_sync); ksyncs[cpu] = rq->scx.kick_sync; should_wait = true; - } else { - cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); } + cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); } resched_curr(rq); @@ -5669,27 +5725,15 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) cpumask_clear_cpu(cpu, this_scx->cpus_to_kick_if_idle); } - if (!should_wait) - return; - - for_each_cpu(cpu, this_scx->cpus_to_wait) { - unsigned long *wait_kick_sync = &cpu_rq(cpu)->scx.kick_sync; - - /* - * Busy-wait until the task running at the time of kicking is no - * longer running. This can be used to implement e.g. core - * scheduling. - * - * smp_cond_load_acquire() pairs with store_releases in - * pick_task_scx() and put_prev_task_scx(). The former breaks - * the wait if SCX's scheduling path is entered even if the same - * task is picked subsequently. The latter is necessary to break - * the wait when $cpu is taken by a higher sched class. - */ - if (cpu != cpu_of(this_rq)) - smp_cond_load_acquire(wait_kick_sync, VAL != ksyncs[cpu]); - - cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); + /* + * Can't wait in hardirq — kick_sync can't advance, deadlocking if + * CPUs wait for each other. Defer to kick_sync_wait_bal_cb(). + */ + if (should_wait) { + raw_spin_rq_lock(this_rq); + this_scx->kick_sync_pending = true; + resched_curr(this_rq); + raw_spin_rq_unlock(this_rq); } } @@ -5794,6 +5838,7 @@ void __init init_sched_ext_class(void) BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_kick_if_idle, GFP_KERNEL, n)); BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_preempt, GFP_KERNEL, n)); BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_wait, GFP_KERNEL, n)); + BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_sync, GFP_KERNEL, n)); rq->scx.deferred_irq_work = IRQ_WORK_INIT_HARD(deferred_irq_workfn); rq->scx.kick_cpus_irq_work = IRQ_WORK_INIT_HARD(kick_cpus_irq_workfn); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 43bbf0693cca40..1ef9ba480f51d1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -805,9 +805,12 @@ struct scx_rq { cpumask_var_t cpus_to_kick_if_idle; cpumask_var_t cpus_to_preempt; cpumask_var_t cpus_to_wait; + cpumask_var_t cpus_to_sync; + bool kick_sync_pending; unsigned long kick_sync; local_t reenq_local_deferred; struct balance_callback deferred_bal_cb; + struct balance_callback kick_sync_bal_cb; struct irq_work deferred_irq_work; struct irq_work kick_cpus_irq_work; struct scx_dispatch_q bypass_dsq; From 090d34f0f0285124452373225bcc520a31e305e4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Mar 2026 14:18:56 -1000 Subject: [PATCH 228/700] selftests/sched_ext: Add cyclic SCX_KICK_WAIT stress test Add a test that creates a 3-CPU kick_wait cycle (A->B->C->A). A BPF scheduler kicks the next CPU in the ring with SCX_KICK_WAIT on every enqueue while userspace workers generate continuous scheduling churn via sched_yield(). Without the preceding fix, this hangs the machine within seconds. Signed-off-by: Tejun Heo Reviewed-by: Christian Loehle Tested-by: Christian Loehle --- tools/testing/selftests/sched_ext/Makefile | 1 + .../sched_ext/cyclic_kick_wait.bpf.c | 68 ++++++ .../selftests/sched_ext/cyclic_kick_wait.c | 194 ++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c create mode 100644 tools/testing/selftests/sched_ext/cyclic_kick_wait.c diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile index 006300ac6dff88..1c9ca328cca1cf 100644 --- a/tools/testing/selftests/sched_ext/Makefile +++ b/tools/testing/selftests/sched_ext/Makefile @@ -188,6 +188,7 @@ auto-test-targets := \ rt_stall \ test_example \ total_bw \ + cyclic_kick_wait \ testcase-targets := $(addsuffix .o,$(addprefix $(SCXOBJ_DIR)/,$(auto-test-targets))) diff --git a/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c b/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c new file mode 100644 index 00000000000000..cb34d3335917ab --- /dev/null +++ b/tools/testing/selftests/sched_ext/cyclic_kick_wait.bpf.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Stress concurrent SCX_KICK_WAIT calls to reproduce wait-cycle deadlock. + * + * Three CPUs are designated from userspace. Every enqueue from one of the + * three CPUs kicks the next CPU in the ring with SCX_KICK_WAIT, creating a + * persistent A -> B -> C -> A wait cycle pressure. + */ +#include + +char _license[] SEC("license") = "GPL"; + +const volatile s32 test_cpu_a; +const volatile s32 test_cpu_b; +const volatile s32 test_cpu_c; + +u64 nr_enqueues; +u64 nr_wait_kicks; + +UEI_DEFINE(uei); + +static s32 target_cpu(s32 cpu) +{ + if (cpu == test_cpu_a) + return test_cpu_b; + if (cpu == test_cpu_b) + return test_cpu_c; + if (cpu == test_cpu_c) + return test_cpu_a; + return -1; +} + +void BPF_STRUCT_OPS(cyclic_kick_wait_enqueue, struct task_struct *p, + u64 enq_flags) +{ + s32 this_cpu = bpf_get_smp_processor_id(); + s32 tgt; + + __sync_fetch_and_add(&nr_enqueues, 1); + + if (p->flags & PF_KTHREAD) { + scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_INF, + enq_flags | SCX_ENQ_PREEMPT); + return; + } + + scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags); + + tgt = target_cpu(this_cpu); + if (tgt < 0 || tgt == this_cpu) + return; + + __sync_fetch_and_add(&nr_wait_kicks, 1); + scx_bpf_kick_cpu(tgt, SCX_KICK_WAIT); +} + +void BPF_STRUCT_OPS(cyclic_kick_wait_exit, struct scx_exit_info *ei) +{ + UEI_RECORD(uei, ei); +} + +SEC(".struct_ops.link") +struct sched_ext_ops cyclic_kick_wait_ops = { + .enqueue = cyclic_kick_wait_enqueue, + .exit = cyclic_kick_wait_exit, + .name = "cyclic_kick_wait", + .timeout_ms = 1000U, +}; diff --git a/tools/testing/selftests/sched_ext/cyclic_kick_wait.c b/tools/testing/selftests/sched_ext/cyclic_kick_wait.c new file mode 100644 index 00000000000000..c2e5aa9de71520 --- /dev/null +++ b/tools/testing/selftests/sched_ext/cyclic_kick_wait.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Test SCX_KICK_WAIT forward progress under cyclic wait pressure. + * + * SCX_KICK_WAIT busy-waits until the target CPU enters the scheduling path. + * If multiple CPUs form a wait cycle (A waits for B, B waits for C, C waits + * for A), all CPUs deadlock unless the implementation breaks the cycle. + * + * This test creates that scenario: three CPUs are arranged in a ring. The BPF + * scheduler's ops.enqueue() kicks the next CPU in the ring with SCX_KICK_WAIT + * on every enqueue. Userspace pins 4 worker threads per CPU that loop calling + * sched_yield(), generating a steady stream of enqueues and thus sustained + * A->B->C->A kick_wait cycle pressure. The test passes if the system remains + * responsive for 5 seconds without the scheduler being killed by the watchdog. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scx_test.h" +#include "cyclic_kick_wait.bpf.skel.h" + +#define WORKERS_PER_CPU 4 +#define NR_TEST_CPUS 3 +#define NR_WORKERS (NR_TEST_CPUS * WORKERS_PER_CPU) + +struct worker_ctx { + pthread_t tid; + int cpu; + volatile bool stop; + volatile __u64 iters; + bool started; +}; + +static void *worker_fn(void *arg) +{ + struct worker_ctx *worker = arg; + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(worker->cpu, &mask); + + if (sched_setaffinity(0, sizeof(mask), &mask)) + return (void *)(uintptr_t)errno; + + while (!worker->stop) { + sched_yield(); + worker->iters++; + } + + return NULL; +} + +static int join_worker(struct worker_ctx *worker) +{ + void *ret; + struct timespec ts; + int err; + + if (!worker->started) + return 0; + + if (clock_gettime(CLOCK_REALTIME, &ts)) + return -errno; + + ts.tv_sec += 2; + err = pthread_timedjoin_np(worker->tid, &ret, &ts); + if (err == ETIMEDOUT) + pthread_detach(worker->tid); + if (err) + return -err; + + if ((uintptr_t)ret) + return -(int)(uintptr_t)ret; + + return 0; +} + +static enum scx_test_status setup(void **ctx) +{ + struct cyclic_kick_wait *skel; + + skel = cyclic_kick_wait__open(); + SCX_FAIL_IF(!skel, "Failed to open skel"); + SCX_ENUM_INIT(skel); + + *ctx = skel; + return SCX_TEST_PASS; +} + +static enum scx_test_status run(void *ctx) +{ + struct cyclic_kick_wait *skel = ctx; + struct worker_ctx workers[NR_WORKERS] = {}; + struct bpf_link *link = NULL; + enum scx_test_status status = SCX_TEST_PASS; + int test_cpus[NR_TEST_CPUS]; + int nr_cpus = 0; + cpu_set_t mask; + int ret, i; + + if (sched_getaffinity(0, sizeof(mask), &mask)) { + SCX_ERR("Failed to get affinity (%d)", errno); + return SCX_TEST_FAIL; + } + + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, &mask)) + test_cpus[nr_cpus++] = i; + if (nr_cpus == NR_TEST_CPUS) + break; + } + + if (nr_cpus < NR_TEST_CPUS) + return SCX_TEST_SKIP; + + skel->rodata->test_cpu_a = test_cpus[0]; + skel->rodata->test_cpu_b = test_cpus[1]; + skel->rodata->test_cpu_c = test_cpus[2]; + + if (cyclic_kick_wait__load(skel)) { + SCX_ERR("Failed to load skel"); + return SCX_TEST_FAIL; + } + + link = bpf_map__attach_struct_ops(skel->maps.cyclic_kick_wait_ops); + if (!link) { + SCX_ERR("Failed to attach scheduler"); + return SCX_TEST_FAIL; + } + + for (i = 0; i < NR_WORKERS; i++) + workers[i].cpu = test_cpus[i / WORKERS_PER_CPU]; + + for (i = 0; i < NR_WORKERS; i++) { + ret = pthread_create(&workers[i].tid, NULL, worker_fn, &workers[i]); + if (ret) { + SCX_ERR("Failed to create worker thread %d (%d)", i, ret); + status = SCX_TEST_FAIL; + goto out; + } + workers[i].started = true; + } + + sleep(5); + + if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) { + SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)", + (unsigned long long)skel->data->uei.kind, + (long long)skel->data->uei.exit_code); + status = SCX_TEST_FAIL; + } + +out: + for (i = 0; i < NR_WORKERS; i++) + workers[i].stop = true; + + for (i = 0; i < NR_WORKERS; i++) { + ret = join_worker(&workers[i]); + if (ret && status == SCX_TEST_PASS) { + SCX_ERR("Failed to join worker thread %d (%d)", i, ret); + status = SCX_TEST_FAIL; + } + } + + if (link) + bpf_link__destroy(link); + + return status; +} + +static void cleanup(void *ctx) +{ + struct cyclic_kick_wait *skel = ctx; + + cyclic_kick_wait__destroy(skel); +} + +struct scx_test cyclic_kick_wait = { + .name = "cyclic_kick_wait", + .description = "Verify SCX_KICK_WAIT forward progress under a 3-CPU wait cycle", + .setup = setup, + .run = run, + .cleanup = cleanup, +}; +REGISTER_SCX_TEST(&cyclic_kick_wait) From 9badc2a84e688be1275bb740942d5f6f51746908 Mon Sep 17 00:00:00 2001 From: Changwoo Min Date: Sun, 29 Mar 2026 16:36:15 +0900 Subject: [PATCH 229/700] PM: EM: Fix NULL pointer dereference when perf domain ID is not found dev_energymodel_nl_get_perf_domains_doit() calls em_perf_domain_get_by_id() but does not check the return value before passing it to __em_nl_get_pd_size(). When a caller supplies a non-existent perf domain ID, em_perf_domain_get_by_id() returns NULL, and __em_nl_get_pd_size() immediately dereferences pd->cpus (struct offset 0x30), causing a NULL pointer dereference. The sister handler dev_energymodel_nl_get_perf_table_doit() already handles this correctly via __em_nl_get_pd_table_id(), which returns NULL and causes the caller to return -EINVAL. Add the same NULL check in the get-perf-domains do handler. Fixes: 380ff27af25e ("PM: EM: Add dump to get-perf-domains in the EM YNL spec") Reported-by: Yi Lai Closes: https://lore.kernel.org/lkml/aXiySM79UYfk+ytd@ly-workstation/ Signed-off-by: Changwoo Min Cc: 6.19+ # 6.19+ [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260329073615.649976-1-changwoo@igalia.com Signed-off-by: Rafael J. Wysocki --- kernel/power/em_netlink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/power/em_netlink.c b/kernel/power/em_netlink.c index 5a611d3950fd51..4d4fd29bd2be9c 100644 --- a/kernel/power/em_netlink.c +++ b/kernel/power/em_netlink.c @@ -109,6 +109,8 @@ int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb, id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]); pd = em_perf_domain_get_by_id(id); + if (!pd) + return -EINVAL; __em_nl_get_pd_size(pd, &msg_sz); msg = genlmsg_new(msg_sz, GFP_KERNEL); From a3ffaa5b397f4df9d6ac16b10583e9df8e6fa471 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 17 Mar 2026 16:34:41 -0400 Subject: [PATCH 230/700] drm/amdgpu/pm: drop SMU driver if version not matched messages It just leads to user confusion. Cc: Yang Wang Cc: Lijo Lazar Reviewed-by: Yang Wang Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit e471627d56272a791972f25e467348b611c31713) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c | 1 - drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c | 1 - drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index 12b052d920f5fe..7ca8fdd23206e3 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -262,7 +262,6 @@ int smu_v11_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(smu->adev->dev, "SMU driver if version not matched\n"); } return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c index 2c20624caca450..ac5e44dff6c975 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c @@ -101,7 +101,6 @@ int smu_v12_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(smu->adev->dev, "SMU driver if version not matched\n"); } return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index cec2df1ad0af54..e38354c694c951 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -284,7 +284,6 @@ int smu_v14_0_check_fw_version(struct smu_context *smu) "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", smu->smc_driver_if_version, if_version, smu_program, smu_version, smu_major, smu_minor, smu_debug); - dev_info(adev->dev, "SMU driver if version not matched\n"); } return ret; From a018d1819f158991b7308e4f74609c6c029b670c Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 24 Mar 2026 17:39:02 +0800 Subject: [PATCH 231/700] drm/amdgpu: validate doorbell_offset in user queue creation amdgpu_userq_get_doorbell_index() passes the user-provided doorbell_offset to amdgpu_doorbell_index_on_bar() without bounds checking. An arbitrarily large doorbell_offset can cause the calculated doorbell index to fall outside the allocated doorbell BO, potentially corrupting kernel doorbell space. Validate that doorbell_offset falls within the doorbell BO before computing the BAR index, using u64 arithmetic to prevent overflow. Fixes: f09c1e6077ab ("drm/amdgpu: generate doorbell index for userqueue") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Alex Deucher (cherry picked from commit de1ef4ffd70e1d15f0bf584fd22b1f28cbd5e2ec) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 7c450350847d6a..0a1b93259887ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -600,6 +600,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } + /* Validate doorbell_offset is within the doorbell BO */ + if ((u64)db_info->doorbell_offset * db_size + db_size > + amdgpu_bo_size(db_obj->obj)) { + r = -EINVAL; + goto unpin_bo; + } + index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), From 62f553d60a801384336f5867967c26ddf3b17038 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Mon, 23 Mar 2026 16:07:02 +0800 Subject: [PATCH 232/700] drm/amdgpu: fix the idr allocation flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the IDR allocation flags by using atomic GFP flags in non‑sleepable contexts to avoid the __might_sleep() complaint. 268.290239] [drm] Initialized amdgpu 3.64.0 for 0000:03:00.0 on minor 0 [ 268.294900] BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323 [ 268.295355] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1744, name: modprobe [ 268.295705] preempt_count: 1, expected: 0 [ 268.295886] RCU nest depth: 0, expected: 0 [ 268.296072] 2 locks held by modprobe/1744: [ 268.296077] #0: ffff8c3a44abd1b8 (&dev->mutex){....}-{4:4}, at: __driver_attach+0xe4/0x210 [ 268.296100] #1: ffffffffc1a6ea78 (amdgpu_pasid_idr_lock){+.+.}-{3:3}, at: amdgpu_pasid_alloc+0x26/0xe0 [amdgpu] [ 268.296494] CPU: 12 UID: 0 PID: 1744 Comm: modprobe Tainted: G U OE 6.19.0-custom #16 PREEMPT(voluntary) [ 268.296498] Tainted: [U]=USER, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE [ 268.296499] Hardware name: AMD Majolica-RN/Majolica-RN, BIOS RMJ1009A 06/13/2021 [ 268.296501] Call Trace: Fixes: 8f1de51f49be ("drm/amdgpu: prevent immediate PASID reuse case") Tested-by: Borislav Petkov (AMD) Signed-off-by: Prike Liang Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ea56aa2625708eaf96f310032391ff37746310ef) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index d88523568b625f..569c5a89ff10b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -68,8 +68,11 @@ int amdgpu_pasid_alloc(unsigned int bits) return -EINVAL; spin_lock(&amdgpu_pasid_idr_lock); + /* TODO: Need to replace the idr with an xarry, and then + * handle the internal locking with ATOMIC safe paths. + */ pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1, - 1U << bits, GFP_KERNEL); + 1U << bits, GFP_ATOMIC); spin_unlock(&amdgpu_pasid_idr_lock); if (pasid >= 0) From 68484a648ade555842e0c75a392f3572b3d51998 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:35 +0530 Subject: [PATCH 233/700] drm/amdkfd: Align expected_queue_size to PAGE_SIZE The AQL queue size can be 4K, but the minimum buffer object (BO) allocation size is PAGE_SIZE. On systems with a page size larger than 4K, the expected queue size does not match the allocated BO size, causing queue creation to fail. Align the expected queue size to PAGE_SIZE so that it matches the allocated BO size and allows queue creation to succeed. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit b01cd158a2f5230b137396c5f8cda3fc780abbc2) --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index bbe869ceae3f0a..e1a922bb2ab75a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -249,10 +249,10 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope topo_dev->node_props.gfx_target_version < 90000) /* metadata_queue_size not supported on GFX7/GFX8 */ expected_queue_size = - properties->queue_size / 2; + PAGE_ALIGN(properties->queue_size / 2); else expected_queue_size = - properties->queue_size + properties->metadata_queue_size; + PAGE_ALIGN(properties->queue_size + properties->metadata_queue_size); vm = drm_priv_to_vm(pdd->drm_priv); err = amdgpu_bo_reserve(vm->root.bo, false); From 6caeace0d1471b33bb43b58893940cc90baca5b9 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:38 +0530 Subject: [PATCH 234/700] drm/amd: Fix MQD and control stack alignment for non-4K MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For gfxV9, due to a hardware bug ("based on the comments in the code here [1]"), the control stack of a user-mode compute queue must be allocated immediately after the page boundary of its regular MQD buffer. To handle this, we allocate an enlarged MQD buffer where the first page is used as the MQD and the remaining pages store the control stack. Although these regions share the same BO, they require different memory types: the MQD must be UC (uncached), while the control stack must be NC (non-coherent), matching the behavior when the control stack is allocated in user space. This logic works correctly on systems where the CPU page size matches the GPU page size (4K). However, the current implementation aligns both the MQD and the control stack to the CPU PAGE_SIZE. On systems with a larger CPU page size, the entire first CPU page is marked UC—even though that page may contain multiple GPU pages. The GPU treats the second 4K GPU page inside that CPU page as part of the control stack, but it is incorrectly mapped as UC. This patch fixes the issue by aligning both the MQD and control stack sizes to the GPU page size (4K). The first 4K page is correctly marked as UC for the MQD, and the remaining GPU pages are marked NC for the control stack. This ensures proper memory type assignment on systems with larger CPU page sizes. [1]: https://elixir.bootlin.com/linux/v6.18/source/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c#L118 Acked-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit 998d6781410de1c4b787fdbf6c56e851ea7fa553) --- drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 44 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 16 ++----- .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 23 ++++++---- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index e2d32c29668af4..bc772ca3dab726 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -403,6 +403,50 @@ void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, drm_dev_exit(idx); } +/** + * amdgpu_gart_map_gfx9_mqd - map mqd and ctrl_stack dma_addresses into GART entries + * + * @adev: amdgpu_device pointer + * @offset: offset into the GPU's gart aperture + * @pages: number of pages to bind + * @dma_addr: DMA addresses of pages + * @flags: page table entry flags + * + * Map the MQD and control stack addresses into GART entries with the correct + * memory types on gfxv9. The MQD occupies the first 4KB and is followed by + * the control stack. The MQD uses UC (uncached) memory, while the control stack + * uses NC (non-coherent) memory. + */ +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags) +{ + uint64_t page_base; + unsigned int i, j, t; + int idx; + uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); + void *dst; + + if (!adev->gart.ptr) + return; + + if (!drm_dev_enter(adev_to_drm(adev), &idx)) + return; + + t = offset / AMDGPU_GPU_PAGE_SIZE; + dst = adev->gart.ptr; + for (i = 0; i < pages; i++) { + page_base = dma_addr[i]; + for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) { + if ((i == 0) && (j == 0)) + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, flags); + else + amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, ctrl_flags); + page_base += AMDGPU_GPU_PAGE_SIZE; + } + } + drm_dev_exit(idx); +} + /** * amdgpu_gart_bind - bind pages into the gart page table * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h index d3118275ddae8f..6ebd2da32ea60b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h @@ -62,6 +62,8 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, void amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags, void *dst); +void amdgpu_gart_map_gfx9_mqd(struct amdgpu_device *adev, uint64_t offset, + int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset, int pages, dma_addr_t *dma_addr, uint64_t flags); void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index eeaa56c8d129fa..0ccb31788b20bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -853,25 +853,15 @@ static void amdgpu_ttm_gart_bind_gfx9_mqd(struct amdgpu_device *adev, int num_xcc = max(1U, adev->gfx.num_xcc_per_xcp); uint64_t page_idx, pages_per_xcc; int i; - uint64_t ctrl_flags = AMDGPU_PTE_MTYPE_VG10(flags, AMDGPU_MTYPE_NC); pages_per_xcc = total_pages; do_div(pages_per_xcc, num_xcc); for (i = 0, page_idx = 0; i < num_xcc; i++, page_idx += pages_per_xcc) { - /* MQD page: use default flags */ - amdgpu_gart_bind(adev, + amdgpu_gart_map_gfx9_mqd(adev, gtt->offset + (page_idx << PAGE_SHIFT), - 1, >t->ttm.dma_address[page_idx], flags); - /* - * Ctrl pages - modify the memory type to NC (ctrl_flags) from - * the second page of the BO onward. - */ - amdgpu_gart_bind(adev, - gtt->offset + ((page_idx + 1) << PAGE_SHIFT), - pages_per_xcc - 1, - >t->ttm.dma_address[page_idx + 1], - ctrl_flags); + pages_per_xcc, >t->ttm.dma_address[page_idx], + flags); } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index d5c234f30e8d64..a535f151cb5fdd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -42,9 +42,16 @@ static uint64_t mqd_stride_v9(struct mqd_manager *mm, struct queue_properties *q) { if (mm->dev->kfd->cwsr_enabled && - q->type == KFD_QUEUE_TYPE_COMPUTE) - return ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE); + q->type == KFD_QUEUE_TYPE_COMPUTE) { + + /* On gfxv9, the MQD resides in the first 4K page, + * followed by the control stack. Align both to + * AMDGPU_GPU_PAGE_SIZE to maintain the required 4K boundary. + */ + + return ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE); + } return mm->mqd_size; } @@ -151,8 +158,8 @@ static struct kfd_mem_obj *allocate_mqd(struct mqd_manager *mm, if (!mqd_mem_obj) return NULL; retval = amdgpu_amdkfd_alloc_kernel_mem(node->adev, - (ALIGN(q->ctl_stack_size, PAGE_SIZE) + - ALIGN(sizeof(struct v9_mqd), PAGE_SIZE)) * + (ALIGN(ALIGN(q->ctl_stack_size, AMDGPU_GPU_PAGE_SIZE) + + ALIGN(sizeof(struct v9_mqd), AMDGPU_GPU_PAGE_SIZE), PAGE_SIZE)) * NUM_XCC(node->xcc_mask), mqd_on_vram(node->adev) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, @@ -360,7 +367,7 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, struct kfd_context_save_area_header header; /* Control stack is located one page after MQD. */ - void *mqd_ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *mqd_ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -397,7 +404,7 @@ static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, voi { struct v9_mqd *m; /* Control stack is located one page after MQD. */ - void *ctl_stack = (void *)((uintptr_t)mqd + PAGE_SIZE); + void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); @@ -443,7 +450,7 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd, *gart_addr = addr; /* Control stack is located one page after MQD. */ - ctl_stack = (void *)((uintptr_t)*mqd + PAGE_SIZE); + ctl_stack = (void *)((uintptr_t)*mqd + AMDGPU_GPU_PAGE_SIZE); memcpy(ctl_stack, ctl_stack_src, ctl_stack_size); m->cp_hqd_pq_doorbell_control = From ced5c30e47d1cd52d6ae40f809223a6286854086 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Sat, 14 Mar 2026 23:33:53 +0800 Subject: [PATCH 235/700] drm/amdgpu/userq: fix memory leak in MQD creation error paths In mes_userq_mqd_create(), the memdup_user() allocations for IP-specific MQD structs are not freed when subsequent VA validation fails. The goto free_mqd label only cleans up the MQD BO object and userq_props. Fix by adding kfree() before each goto free_mqd on VA validation failure in the COMPUTE, GFX, and SDMA branches. Fixes: 9e46b8bb0539 ("drm/amdgpu: validate userq buffer virtual address and size") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Reviewed-by: Prike Liang Signed-off-by: Alex Deucher (cherry picked from commit 27f5ff9e4a4150d7cf8b4085aedd3b77ddcc5d08) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 8c74894254f7ac..faac21ee5739c0 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -324,8 +324,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, 2048); - if (r) + if (r) { + kfree(compute_mqd); goto free_mqd; + } userq_props->eop_gpu_addr = compute_mqd->eop_va; userq_props->hqd_pipe_priority = AMDGPU_GFX_PIPE_PRIO_NORMAL; @@ -365,12 +367,16 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, shadow_info.shadow_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, shadow_info.csa_size); - if (r) + if (r) { + kfree(mqd_gfx_v11); goto free_mqd; + } kfree(mqd_gfx_v11); } else if (queue->queue_type == AMDGPU_HW_IP_DMA) { @@ -390,8 +396,10 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, 32); - if (r) + if (r) { + kfree(mqd_sdma_v11); goto free_mqd; + } userq_props->csa_addr = mqd_sdma_v11->csa_va; kfree(mqd_sdma_v11); From 4487571ef17a30d274600b3bd6965f497a881299 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Thu, 26 Mar 2026 17:51:28 +0530 Subject: [PATCH 236/700] drm/amdgpu: Change AMDGPU_VA_RESERVED_TRAP_SIZE to 64KB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, AMDGPU_VA_RESERVED_TRAP_SIZE is hardcoded to 8KB, while KFD_CWSR_TBA_TMA_SIZE is defined as 2 * PAGE_SIZE. On systems with 4K pages, both values match (8KB), so allocation and reserved space are consistent. However, on 64K page-size systems, KFD_CWSR_TBA_TMA_SIZE becomes 128KB, while the reserved trap area remains 8KB. This mismatch causes the kernel to crash when running rocminfo or rccl unit tests. Kernel attempted to read user page (2) - exploit attempt? (uid: 1001) BUG: Kernel NULL pointer dereference on read at 0x00000002 Faulting instruction address: 0xc0000000002c8a64 Oops: Kernel access of bad area, sig: 11 [#1] LE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA pSeries CPU: 34 UID: 1001 PID: 9379 Comm: rocminfo Tainted: G E 6.19.0-rc4-amdgpu-00320-gf23176405700 #56 VOLUNTARY Tainted: [E]=UNSIGNED_MODULE Hardware name: IBM,9105-42A POWER10 (architected) 0x800200 0xf000006 of:IBM,FW1060.30 (ML1060_896) hv:phyp pSeries NIP: c0000000002c8a64 LR: c00000000125dbc8 CTR: c00000000125e730 REGS: c0000001e0957580 TRAP: 0300 Tainted: G E MSR: 8000000000009033 CR: 24008268 XER: 00000036 CFAR: c00000000125dbc4 DAR: 0000000000000002 DSISR: 40000000 IRQMASK: 1 GPR00: c00000000125d908 c0000001e0957820 c0000000016e8100 c00000013d814540 GPR04: 0000000000000002 c00000013d814550 0000000000000045 0000000000000000 GPR08: c00000013444d000 c00000013d814538 c00000013d814538 0000000084002268 GPR12: c00000000125e730 c000007e2ffd5f00 ffffffffffffffff 0000000000020000 GPR16: 0000000000000000 0000000000000002 c00000015f653000 0000000000000000 GPR20: c000000138662400 c00000013d814540 0000000000000000 c00000013d814500 GPR24: 0000000000000000 0000000000000002 c0000001e0957888 c0000001e0957878 GPR28: c00000013d814548 0000000000000000 c00000013d814540 c0000001e0957888 NIP [c0000000002c8a64] __mutex_add_waiter+0x24/0xc0 LR [c00000000125dbc8] __mutex_lock.constprop.0+0x318/0xd00 Call Trace: 0xc0000001e0957890 (unreliable) __mutex_lock.constprop.0+0x58/0xd00 amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu+0x6fc/0xb60 [amdgpu] kfd_process_alloc_gpuvm+0x54/0x1f0 [amdgpu] kfd_process_device_init_cwsr_dgpu+0xa4/0x1a0 [amdgpu] kfd_process_device_init_vm+0xd8/0x2e0 [amdgpu] kfd_ioctl_acquire_vm+0xd0/0x130 [amdgpu] kfd_ioctl+0x514/0x670 [amdgpu] sys_ioctl+0x134/0x180 system_call_exception+0x114/0x300 system_call_vectored_common+0x15c/0x2ec This patch changes AMDGPU_VA_RESERVED_TRAP_SIZE to 64 KB and KFD_CWSR_TBA_TMA_SIZE to the AMD GPU page size. This means we reserve 64 KB for the trap in the address space, but only allocate 8 KB within it. With this approach, the allocation size never exceeds the reserved area. Fixes: 34a1de0f7935 ("drm/amdkfd: Relocate TBA/TMA to opposite side of VM hole") Reviewed-by: Christian König Suggested-by: Felix Kuehling Suggested-by: Christian König Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit 31b8de5e55666f26ea7ece5f412b83eab3f56dbb) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index bb276c0ad06dd6..d5b7061556baea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -173,7 +173,7 @@ struct amdgpu_bo_vm; #define AMDGPU_VA_RESERVED_SEQ64_SIZE (2ULL << 20) #define AMDGPU_VA_RESERVED_SEQ64_START(adev) (AMDGPU_VA_RESERVED_CSA_START(adev) \ - AMDGPU_VA_RESERVED_SEQ64_SIZE) -#define AMDGPU_VA_RESERVED_TRAP_SIZE (2ULL << 12) +#define AMDGPU_VA_RESERVED_TRAP_SIZE (1ULL << 16) #define AMDGPU_VA_RESERVED_TRAP_START(adev) (AMDGPU_VA_RESERVED_SEQ64_START(adev) \ - AMDGPU_VA_RESERVED_TRAP_SIZE) #define AMDGPU_VA_RESERVED_BOTTOM (1ULL << 16) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index e5b56412931bd6..035687a17d896f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -102,8 +102,8 @@ * The first chunk is the TBA used for the CWSR ISA code. The second * chunk is used as TMA for user-mode trap handler setup in daisy-chain mode. */ -#define KFD_CWSR_TBA_TMA_SIZE (PAGE_SIZE * 2) -#define KFD_CWSR_TMA_OFFSET (PAGE_SIZE + 2048) +#define KFD_CWSR_TBA_TMA_SIZE (AMDGPU_GPU_PAGE_SIZE * 2) +#define KFD_CWSR_TMA_OFFSET (AMDGPU_GPU_PAGE_SIZE + 2048) #define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \ (KFD_MAX_NUM_OF_PROCESSES * \ From e927b36ae18b66b49219eaa9f46edc7b4fdbb25e Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Sat, 21 Mar 2026 17:25:14 +0530 Subject: [PATCH 237/700] drm/amd/display: Fix NULL pointer dereference in dcn401_init_hw() dcn401_init_hw() assumes that update_bw_bounding_box() is valid when entering the update path. However, the existing condition: ((!fams2_enable && update_bw_bounding_box) || freq_changed) does not guarantee this, as the freq_changed branch can evaluate to true independently of the callback pointer. This can result in calling update_bw_bounding_box() when it is NULL. Fix this by separating the update condition from the pointer checks and ensuring the callback, dc->clk_mgr, and bw_params are validated before use. Fixes the below: ../dc/hwss/dcn401/dcn401_hwseq.c:367 dcn401_init_hw() error: we previously assumed 'dc->res_pool->funcs->update_bw_bounding_box' could be null (see line 362) Fixes: ca0fb243c3bb ("drm/amd/display: Underflow Seen on DCN401 eGPU") Cc: Daniel Sa Cc: Alvin Lee Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Dan Carpenter Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher (cherry picked from commit 86117c5ab42f21562fedb0a64bffea3ee5fcd477) Cc: stable@vger.kernel.org --- .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index eb198d52a1155c..4dfb6c8658312d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -147,6 +147,7 @@ void dcn401_init_hw(struct dc *dc) int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; + bool dchub_ref_freq_changed; int current_dchub_ref_freq = 0; if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { @@ -360,14 +361,18 @@ void dcn401_init_hw(struct dc *dc) dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; + + /* sw and fw FAMS versions must match for support */ dc->debug.fams2_config.bits.enable &= - dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support - if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box) - || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) { + dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; + dchub_ref_freq_changed = + res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq; + if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) && + dc->res_pool->funcs->update_bw_bounding_box && + dc->clk_mgr && dc->clk_mgr->bw_params) { /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ - if (dc->clk_mgr) - dc->res_pool->funcs->update_bw_bounding_box(dc, - dc->clk_mgr->bw_params); + dc->res_pool->funcs->update_bw_bounding_box(dc, + dc->clk_mgr->bw_params); } } } From 39e2a5bf970402a8530a319cf06122e216ba57b8 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Thu, 26 Mar 2026 22:45:23 +0000 Subject: [PATCH 238/700] hwmon: (occ) Fix division by zero in occ_show_power_1() In occ_show_power_1() case 1, the accumulator is divided by update_tag without checking for zero. If no samples have been collected yet (e.g. during early boot when the sensor block is included but hasn't been updated), update_tag is zero, causing a kernel divide-by-zero crash. The 2019 fix in commit 211186cae14d ("hwmon: (occ) Fix division by zero issue") only addressed occ_get_powr_avg() used by occ_show_power_2() and occ_show_power_a0(). This separate code path in occ_show_power_1() was missed. Fix this by reusing the existing occ_get_powr_avg() helper, which already handles the zero-sample case and uses mul_u64_u32_div() to multiply before dividing for better precision. Move the helper above occ_show_power_1() so it is visible at the call site. Fixes: c10e753d43eb ("hwmon (occ): Add sensor types and versions") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260326224510.294619-2-sanman.pradhan@hpe.com [groeck: Fix alignment problems reported by checkpatch] Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 89928d38831b61..ec7dbb94de0b7f 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -420,6 +420,12 @@ static ssize_t occ_show_freq_2(struct device *dev, return sysfs_emit(buf, "%u\n", val); } +static u64 occ_get_powr_avg(u64 accum, u32 samples) +{ + return (samples == 0) ? 0 : + mul_u64_u32_div(accum, 1000000UL, samples); +} + static ssize_t occ_show_power_1(struct device *dev, struct device_attribute *attr, char *buf) { @@ -441,9 +447,8 @@ static ssize_t occ_show_power_1(struct device *dev, val = get_unaligned_be16(&power->sensor_id); break; case 1: - val = get_unaligned_be32(&power->accumulator) / - get_unaligned_be32(&power->update_tag); - val *= 1000000ULL; + val = occ_get_powr_avg(get_unaligned_be32(&power->accumulator), + get_unaligned_be32(&power->update_tag)); break; case 2: val = (u64)get_unaligned_be32(&power->update_tag) * @@ -459,12 +464,6 @@ static ssize_t occ_show_power_1(struct device *dev, return sysfs_emit(buf, "%llu\n", val); } -static u64 occ_get_powr_avg(u64 accum, u32 samples) -{ - return (samples == 0) ? 0 : - mul_u64_u32_div(accum, 1000000UL, samples); -} - static ssize_t occ_show_power_2(struct device *dev, struct device_attribute *attr, char *buf) { From daf470b8882b6f7f53cbfe9ec2b93a1b21528cdc Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Fri, 27 Mar 2026 14:29:17 +0530 Subject: [PATCH 239/700] drm/amdgpu: Fix wait after reset sequence in S4 For a mode-1 reset done at the end of S4 on PSPv11 dGPUs, only check if TOS is unloaded. Fixes: 32f73741d6ee ("drm/amdgpu: Wait for bootloader after PSPv11 reset") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/4853 Signed-off-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2fb4883b884a437d760bd7bdf7695a7e5a60bba3) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++++++-- drivers/gpu/drm/amd/amdgpu/psp_v11_0.c | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 95d26f086d5456..c91638e6517464 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2703,8 +2703,12 @@ static int amdgpu_pmops_freeze(struct device *dev) if (r) return r; - if (amdgpu_acpi_should_gpu_reset(adev)) - return amdgpu_asic_reset(adev); + if (amdgpu_acpi_should_gpu_reset(adev)) { + amdgpu_device_lock_reset_domain(adev->reset_domain); + r = amdgpu_asic_reset(adev); + amdgpu_device_unlock_reset_domain(adev->reset_domain); + return r; + } return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 9aa988982304b3..fb7aaf5ae05cfb 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -170,7 +170,8 @@ static int psp_v11_0_wait_for_bootloader(struct psp_context *psp) int retry_loop; /* For a reset done at the end of S3, only wait for TOS to be unloaded */ - if (adev->in_s3 && !(adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev)) + if ((adev->in_s4 || adev->in_s3) && !(adev->flags & AMD_IS_APU) && + amdgpu_in_reset(adev)) return psp_v11_wait_for_tos_unload(psp); for (retry_loop = 0; retry_loop < 20; retry_loop++) { From 09773978879ecf71a7990fe9a28ce4eb92bce645 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Thu, 26 Mar 2026 22:45:29 +0000 Subject: [PATCH 240/700] hwmon: (occ) Fix missing newline in occ_show_extended() In occ_show_extended() case 0, when the EXTN_FLAG_SENSOR_ID flag is set, the sysfs_emit format string "%u" is missing the trailing newline that the sysfs ABI expects. The else branch correctly uses "%4phN\n", and all other show functions in this file include the trailing newline. Add the missing "\n" for consistency and correct sysfs output. Fixes: c10e753d43eb ("hwmon (occ): Add sensor types and versions") Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260326224510.294619-3-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index ec7dbb94de0b7f..42cc6068bb08be 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -724,7 +724,7 @@ static ssize_t occ_show_extended(struct device *dev, switch (sattr->nr) { case 0: if (extn->flags & EXTN_FLAG_SENSOR_ID) { - rc = sysfs_emit(buf, "%u", + rc = sysfs_emit(buf, "%u\n", get_unaligned_be32(&extn->sensor_id)); } else { rc = sysfs_emit(buf, "%4phN\n", extn->name); From 78746a474e92fc7aaed12219bec7c78ae1bd6156 Mon Sep 17 00:00:00 2001 From: Donet Tom Date: Mon, 23 Mar 2026 09:58:39 +0530 Subject: [PATCH 241/700] drm/amdkfd: Fix queue preemption/eviction failures by aligning control stack size to GPU page size The control stack size is calculated based on the number of CUs and waves, and is then aligned to PAGE_SIZE. When the resulting control stack size is aligned to 64 KB, GPU hangs and queue preemption failures are observed while running RCCL unit tests on systems with more than two GPUs. amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: GPU reset begin!. Source: 4 amdgpu 0048:0f:00.0: amdgpu: Queue preemption failed for queue with doorbell_id: 80030008 amdgpu 0048:0f:00.0: amdgpu: Failed to evict process queues amdgpu 0048:0f:00.0: amdgpu: Failed to restore process queues This issue is observed on both 4 KB and 64 KB system page-size configurations. This patch fixes the issue by aligning the control stack size to AMDGPU_GPU_PAGE_SIZE instead of PAGE_SIZE, so the control stack size will not be 64 KB on systems with a 64 KB page size and queue preemption works correctly. Additionally, In the current code, wg_data_size is aligned to PAGE_SIZE, which can waste memory if the system page size is large. In this patch, wg_data_size is aligned to AMDGPU_GPU_PAGE_SIZE. The cwsr_size, calculated from wg_data_size and the control stack size, is aligned to PAGE_SIZE. Reviewed-by: Felix Kuehling Signed-off-by: Donet Tom Signed-off-by: Alex Deucher (cherry picked from commit a3e14436304392fbada359edd0f1d1659850c9b7) --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index e1a922bb2ab75a..28354a4e5dd5d1 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -492,10 +492,11 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) cu_num = props->simd_count / props->simd_per_cu / NUM_XCC(dev->gpu->xcc_mask); wave_num = get_num_waves(props, gfxv, cu_num); - wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), PAGE_SIZE); + wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv, props), + AMDGPU_GPU_PAGE_SIZE); ctl_stack_size = wave_num * CNTL_STACK_BYTES_PER_WAVE(gfxv) + 8; ctl_stack_size = ALIGN(SIZEOF_HSA_USER_CONTEXT_SAVE_AREA_HEADER + ctl_stack_size, - PAGE_SIZE); + AMDGPU_GPU_PAGE_SIZE); if ((gfxv / 10000 * 10000) == 100000) { /* HW design limits control stack size to 0x7000. @@ -507,7 +508,7 @@ void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev) props->ctl_stack_size = ctl_stack_size; props->debug_memory_size = ALIGN(wave_num * DEBUGGER_BYTES_PER_WAVE, DEBUGGER_BYTES_ALIGN); - props->cwsr_size = ctl_stack_size + wg_data_size; + props->cwsr_size = ALIGN(ctl_stack_size + wg_data_size, PAGE_SIZE); if (gfxv == 80002) /* GFX_VERSION_TONGA */ props->eop_buffer_size = 0x8000; From 59e1be1278f064d7172b00473b7e0c453cb1ec52 Mon Sep 17 00:00:00 2001 From: Emanuele Ghidoli Date: Fri, 13 Mar 2026 14:52:31 +0100 Subject: [PATCH 242/700] spi: cadence-qspi: Fix exec_mem_op error handling cqspi_exec_mem_op() increments the runtime PM usage counter before all refcount checks are performed. If one of these checks fails, the function returns without dropping the PM reference. Move the pm_runtime_resume_and_get() call after the refcount checks so that runtime PM is only acquired when the operation can proceed and drop the inflight_ops refcount if the PM resume fails. Cc: stable@vger.kernel.org Fixes: 7446284023e8 ("spi: cadence-quadspi: Implement refcount to handle unbind during busy") Signed-off-by: Emanuele Ghidoli Link: https://patch.msgid.link/20260313135236.46642-1-ghidoliemanuele@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 5fb0cb07c110cc..2ead419e896e2d 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1483,14 +1483,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) if (refcount_read(&cqspi->inflight_ops) == 0) return -ENODEV; - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(&mem->spi->dev, "resume failed with %d\n", ret); - return ret; - } - } - if (!refcount_read(&cqspi->refcount)) return -EBUSY; @@ -1502,6 +1494,14 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) return -EBUSY; } + if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(&mem->spi->dev, "resume failed with %d\n", ret); + goto dec_inflight_refcount; + } + } + ret = cqspi_mem_process(mem, op); if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) @@ -1510,6 +1510,7 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) if (ret) dev_err(&mem->spi->dev, "operation failed with %d\n", ret); +dec_inflight_refcount: if (refcount_read(&cqspi->inflight_ops) > 1) refcount_dec(&cqspi->inflight_ops); From fd63f185979b047fb22a0dfc6bd94d0cab6a6a70 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 27 Mar 2026 10:52:57 +0100 Subject: [PATCH 243/700] ipv6: prevent possible UaF in addrconf_permanent_addr() The mentioned helper try to warn the user about an exceptional condition, but the message is delivered too late, accessing the ipv6 after its possible deletion. Reorder the statement to avoid the possible UaF; while at it, place the warning outside the idev->lock as it needs no protection. Reported-by: Jakub Kicinski Closes: https://sashiko.dev/#/patchset/8c8bfe2e1a324e501f0e15fef404a77443fd8caf.1774365668.git.pabeni%40redhat.com Fixes: f1705ec197e7 ("net: ipv6: Make address flushing on ifdown optional") Signed-off-by: Paolo Abeni Link: https://patch.msgid.link/ef973c3a8cb4f8f1787ed469f3e5391b9fe95aa0.1774601542.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski --- net/ipv6/addrconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f4e23b543585f8..dd0b4d80e0f84d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3625,12 +3625,12 @@ static void addrconf_permanent_addr(struct net *net, struct net_device *dev) if ((ifp->flags & IFA_F_PERMANENT) && fixup_permanent_addr(net, idev, ifp) < 0) { write_unlock_bh(&idev->lock); - in6_ifa_hold(ifp); - ipv6_del_addr(ifp); - write_lock_bh(&idev->lock); net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", idev->dev->name, &ifp->addr); + in6_ifa_hold(ifp); + ipv6_del_addr(ifp); + write_lock_bh(&idev->lock); } } From 514aac3599879a7ed48b7dc19e31145beb6958ac Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 27 Mar 2026 10:48:21 +0100 Subject: [PATCH 244/700] net: airoha: Add missing cleanup bits in airoha_qdma_cleanup_rx_queue() In order to properly cleanup hw rx QDMA queues and bring the device to the initial state, reset rx DMA queue head/tail index. Moreover, reset queued DMA descriptor fields. Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") Tested-by: Madhur Agrawal Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260327-airoha_qdma_cleanup_rx_queue-fix-v1-1-369d6ab1511a@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 56cf9a926a83da..c2a54dbcbb0d57 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -794,18 +794,34 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) { - struct airoha_eth *eth = q->qdma->eth; + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0]; while (q->queued) { struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; struct page *page = virt_to_head_page(e->buf); dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, page_pool_get_dma_dir(q->page_pool)); page_pool_put_full_page(q->page_pool, page, false); + /* Reset DMA descriptor */ + WRITE_ONCE(desc->ctrl, 0); + WRITE_ONCE(desc->addr, 0); + WRITE_ONCE(desc->data, 0); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); + q->tail = (q->tail + 1) % q->ndesc; q->queued--; } + + q->head = q->tail; + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail)); } static int airoha_qdma_init_rx(struct airoha_qdma *qdma) From ddc748a391dd8642ba6b2e4fe22e7f2ddf84b7f0 Mon Sep 17 00:00:00 2001 From: Guoyu Su Date: Fri, 27 Mar 2026 23:35:07 +0800 Subject: [PATCH 245/700] net: use skb_header_pointer() for TCPv4 GSO frag_off check Syzbot reported a KMSAN uninit-value warning in gso_features_check() called from netif_skb_features() [1]. gso_features_check() reads iph->frag_off to decide whether to clear mangleid_features. Accessing the IPv4 header via ip_hdr()/inner_ip_hdr() can rely on skb header offsets that are not always safe for direct dereference on packets injected from PF_PACKET paths. Use skb_header_pointer() for the TCPv4 frag_off check so the header read is robust whether data is already linear or needs copying. [1] https://syzkaller.appspot.com/bug?extid=1543a7d954d9c6d00407 Link: https://lore.kernel.org/netdev/willemdebruijn.kernel.1a9f35039caab@gmail.com/ Fixes: cbc53e08a793 ("GSO: Add GSO type for fixed IPv4 ID") Reported-by: syzbot+1543a7d954d9c6d00407@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=1543a7d954d9c6d00407 Tested-by: syzbot+1543a7d954d9c6d00407@syzkaller.appspotmail.com Signed-off-by: Guoyu Su Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260327153507.39742-1-yss2813483011xxl@gmail.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index fc555706241436..831129f2a69b50 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3821,10 +3821,15 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb, * segmentation-offloads.rst). */ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { - struct iphdr *iph = skb->encapsulation ? - inner_ip_hdr(skb) : ip_hdr(skb); + const struct iphdr *iph; + struct iphdr _iph; + int nhoff = skb->encapsulation ? + skb_inner_network_offset(skb) : + skb_network_offset(skb); - if (!(iph->frag_off & htons(IP_DF))) + iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); + + if (!iph || !(iph->frag_off & htons(IP_DF))) features &= ~dev->mangleid_features; } From e6e3eb5ee89ac4c163d46429391c889a1bb5e404 Mon Sep 17 00:00:00 2001 From: Yochai Eisenrich Date: Sun, 29 Mar 2026 00:14:36 +0300 Subject: [PATCH 246/700] net: sched: cls_api: fix tc_chain_fill_node to initialize tcm_info to zero to prevent an info-leak When building netlink messages, tc_chain_fill_node() never initializes the tcm_info field of struct tcmsg. Since the allocation is not zeroed, kernel heap memory is leaked to userspace through this 4-byte field. The fix simply zeroes tcm_info alongside the other fields that are already initialized. Fixes: 32a4f5ecd738 ("net: sched: introduce chain object to uapi") Signed-off-by: Yochai Eisenrich Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260328211436.1010152-1-echelonh@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/cls_api.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 4829c27446e336..20f7f9ee0b353a 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -2969,6 +2969,7 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, tcm->tcm__pad1 = 0; tcm->tcm__pad2 = 0; tcm->tcm_handle = 0; + tcm->tcm_info = 0; if (block->q) { tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex; tcm->tcm_parent = block->q->handle; From cedc1bf327de62ec30af9743bd1f601c2de30553 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 29 Mar 2026 12:32:27 +0200 Subject: [PATCH 247/700] net: airoha: Delay offloading until all net_devices are fully registered Netfilter flowtable can theoretically try to offload flower rules as soon as a net_device is registered while all the other ones are not registered or initialized, triggering a possible NULL pointer dereferencing of qdma pointer in airoha_ppe_set_cpu_port routine. Moreover, if register_netdev() fails for a particular net_device, there is a small race if Netfilter tries to offload flowtable rules before all the net_devices are properly unregistered in airoha_probe() error patch, triggering a NULL pointer dereferencing in airoha_ppe_set_cpu_port routine. In order to avoid any possible race, delay offloading until all net_devices are registered in the networking subsystem. Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260329-airoha-regiser-race-fix-v2-1-f4ebb139277b@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 2 ++ drivers/net/ethernet/airoha/airoha_eth.h | 1 + drivers/net/ethernet/airoha/airoha_ppe.c | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index c2a54dbcbb0d57..95ba99b89428e4 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2962,6 +2962,8 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth) return err; } + set_bit(DEV_STATE_REGISTERED, ð->state); + return 0; } diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 20e602d61e615e..a97903569335ff 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -88,6 +88,7 @@ enum { enum { DEV_STATE_INITIALIZED, + DEV_STATE_REGISTERED, }; enum { diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 5724f8f2defd51..c2c32b6833df94 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1368,6 +1368,13 @@ int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data) struct airoha_eth *eth = ppe->eth; int err = 0; + /* Netfilter flowtable can try to offload flower rules while not all + * the net_devices are registered or initialized. Delay offloading + * until all net_devices are registered in the system. + */ + if (!test_bit(DEV_STATE_REGISTERED, ð->state)) + return -EBUSY; + mutex_lock(&flow_offload_mutex); if (!eth->npu) From 4ee937107d52f9e5c350e4b5e629760e328b3d9f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 29 Mar 2026 07:43:56 +0800 Subject: [PATCH 248/700] bnxt_en: set backing store type from query type bnxt_hwrm_func_backing_store_qcaps_v2() stores resp->type from the firmware response in ctxm->type and later uses that value to index fixed backing-store metadata arrays such as ctx_arr[] and bnxt_bstore_to_trace[]. ctxm->type is fixed by the current backing-store query type and matches the array index of ctx->ctx_arr. Set ctxm->type from the current loop variable instead of depending on resp->type. Also update the loop to advance type from next_valid_type in the for statement, which keeps the control flow simpler for non-valid and unchanged entries. Fixes: 6a4d0774f02d ("bnxt_en: Add support for new backing store query firmware API") Signed-off-by: Pengpeng Hou Reviewed-by: Michael Chan Tested-by: Michael Chan Link: https://patch.msgid.link/20260328234357.43669-1-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0751c0e4581a21..7ed805713fbbe0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8671,7 +8671,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) struct hwrm_func_backing_store_qcaps_v2_output *resp; struct hwrm_func_backing_store_qcaps_v2_input *req; struct bnxt_ctx_mem_info *ctx = bp->ctx; - u16 type; + u16 type, next_type = 0; int rc; rc = hwrm_req_init(bp, req, HWRM_FUNC_BACKING_STORE_QCAPS_V2); @@ -8687,7 +8687,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) resp = hwrm_req_hold(bp, req); - for (type = 0; type < BNXT_CTX_V2_MAX; ) { + for (type = 0; type < BNXT_CTX_V2_MAX; type = next_type) { struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; u8 init_val, init_off, i; u32 max_entries; @@ -8700,7 +8700,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) if (rc) goto ctx_done; flags = le32_to_cpu(resp->flags); - type = le16_to_cpu(resp->next_valid_type); + next_type = le16_to_cpu(resp->next_valid_type); if (!(flags & BNXT_CTX_MEM_TYPE_VALID)) { bnxt_free_one_ctx_mem(bp, ctxm, true); continue; @@ -8715,7 +8715,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp) else continue; } - ctxm->type = le16_to_cpu(resp->type); + ctxm->type = type; ctxm->entry_size = entry_size; ctxm->flags = flags; ctxm->instance_bmap = le32_to_cpu(resp->instance_bit_map); From 316fb1b3169efb081d2db910cbbfef445afa03b9 Mon Sep 17 00:00:00 2001 From: robbieko Date: Wed, 25 Mar 2026 18:18:15 +0800 Subject: [PATCH 249/700] btrfs: fix incorrect return value after changing leaf in lookup_extent_data_ref() After commit 1618aa3c2e01 ("btrfs: simplify return variables in lookup_extent_data_ref()"), the err and ret variables were merged into a single ret variable. However, when btrfs_next_leaf() returns 0 (success), ret is overwritten from -ENOENT to 0. If the first key in the next leaf does not match (different objectid or type), the function returns 0 instead of -ENOENT, making the caller believe the lookup succeeded when it did not. This can lead to operations on the wrong extent tree item, potentially causing extent tree corruption. Fix this by returning -ENOENT directly when the key does not match, instead of relying on the ret variable. Fixes: 1618aa3c2e01 ("btrfs: simplify return variables in lookup_extent_data_ref()") CC: stable@vger.kernel.org # 6.12+ Reviewed-by: Filipe Manana Signed-off-by: robbieko Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 85ee5c79759d8f..098e64106d0229 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -495,7 +495,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid != bytenr || key.type != BTRFS_EXTENT_DATA_REF_KEY) - return ret; + return -ENOENT; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_data_ref); From e08e0754e690e4909cab83ac43fd2c93c6200514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 25 Mar 2026 15:58:44 +0200 Subject: [PATCH 250/700] drm/i915/cdclk: Do the full CDCLK dance for min_voltage_level changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently I forgot about the pipe min_voltage_level when I decoupled the CDCLK calculations from modesets. Even if the CDCLK frequency doesn't need changing we may still need to bump the voltage level to accommodate an increase in the port clock frequency. Currently, even if there is a full modeset, we won't notice the need to go through the full CDCLK calculations/programming, unless the set of enabled/active pipes changes, or the pipe/dbuf min CDCLK changes. Duplicate the same logic we use the pipe's min CDCLK frequency to also deal with its min voltage level. Note that the 'allow_voltage_level_decrease' stuff isn't really useful here since the min voltage level can only change during a full modeset. But I think sticking to the same approach in the three similar parts (pipe min cdclk, pipe min voltage level, dbuf min cdclk) is a good idea. Cc: stable@vger.kernel.org Tested-by: Mikhail Rudenko Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15826 Fixes: ba91b9eecb47 ("drm/i915/cdclk: Decouple cdclk from state->modeset") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260325135849.12603-2-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak (cherry picked from commit 0f21a14987ebae3c05ad1184ea872e7b7a7b8695) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_cdclk.c | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index f5946e677c93a7..3d7b4b0795cdfc 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2971,6 +2971,53 @@ static int intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state, return 0; } +static int intel_cdclk_update_crtc_min_voltage_level(struct intel_atomic_state *state, + struct intel_crtc *crtc, + u8 old_min_voltage_level, + u8 new_min_voltage_level, + bool *need_cdclk_calc) +{ + struct intel_display *display = to_intel_display(state); + struct intel_cdclk_state *cdclk_state; + bool allow_voltage_level_decrease = intel_any_crtc_needs_modeset(state); + int ret; + + if (new_min_voltage_level == old_min_voltage_level) + return 0; + + if (!allow_voltage_level_decrease && + new_min_voltage_level < old_min_voltage_level) + return 0; + + cdclk_state = intel_atomic_get_cdclk_state(state); + if (IS_ERR(cdclk_state)) + return PTR_ERR(cdclk_state); + + old_min_voltage_level = cdclk_state->min_voltage_level[crtc->pipe]; + + if (new_min_voltage_level == old_min_voltage_level) + return 0; + + if (!allow_voltage_level_decrease && + new_min_voltage_level < old_min_voltage_level) + return 0; + + cdclk_state->min_voltage_level[crtc->pipe] = new_min_voltage_level; + + ret = intel_atomic_lock_global_state(&cdclk_state->base); + if (ret) + return ret; + + *need_cdclk_calc = true; + + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] min voltage level: %d -> %d\n", + crtc->base.base.id, crtc->base.name, + old_min_voltage_level, new_min_voltage_level); + + return 0; +} + int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state, int old_min_cdclk, int new_min_cdclk, bool *need_cdclk_calc) @@ -3386,6 +3433,13 @@ static int intel_crtcs_calc_min_cdclk(struct intel_atomic_state *state, need_cdclk_calc); if (ret) return ret; + + ret = intel_cdclk_update_crtc_min_voltage_level(state, crtc, + old_crtc_state->min_voltage_level, + new_crtc_state->min_voltage_level, + need_cdclk_calc); + if (ret) + return ret; } return 0; From 9c9a57e4e337f94e23ddf69263fd0685c91155fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 25 Mar 2026 15:58:45 +0200 Subject: [PATCH 251/700] drm/i915/dp: Use crtc_state->enhanced_framing properly on ivb/hsw CPU eDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like I missed the drm_dp_enhanced_frame_cap() in the ivb/hsw CPU eDP code when I introduced crtc_state->enhanced_framing. Fix it up so that the state we program to the hardware is guaranteed to match what we computed earlier. Cc: stable@vger.kernel.org Fixes: 3072a24c778a ("drm/i915: Introduce crtc_state->enhanced_framing") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260325135849.12603-3-ville.syrjala@linux.intel.com Reviewed-by: Michał Grzelak (cherry picked from commit 799fe8dc2af52f35c78c4ac97f8e34994dfd8760) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/g4x_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 4cb753177fd8e1..c7fa014e0d50b9 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -137,7 +137,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder, intel_dp->DP |= DP_SYNC_VS_HIGH; intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; - if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) + if (pipe_config->enhanced_framing) intel_dp->DP |= DP_ENHANCED_FRAMING; intel_dp->DP |= DP_PIPE_SEL_IVB(crtc->pipe); From b981e9e94c687b7b19ae8820963f005b842cb2f2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 29 Mar 2026 19:27:48 -0700 Subject: [PATCH 252/700] x86/platform/geode: Fix on-stack property data use-after-return bug The PROPERTY_ENTRY_GPIO macro (and by extension PROPERTY_ENTRY_REF) creates a temporary software_node_ref_args structure on the stack when used in a runtime assignment. This results in the property pointing to data that is invalid once the function returns. Fix this by ensuring the GPIO reference data is not stored on stack and using PROPERTY_ENTRY_REF_ARRAY_LEN() to point directly to the persistent reference data. Fixes: 298c9babadb8 ("x86/platform/geode: switch GPIO buttons and LEDs to software properties") Signed-off-by: Dmitry Torokhov Signed-off-by: Ingo Molnar Cc: Rafael J. Wysocki Cc: Andy Shevchenko Cc: Daniel Scally Cc: Danilo Krummrich Cc: Hans de Goede Cc: Heikki Krogerus Cc: Sakari Ailus Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260329-property-gpio-fix-v2-1-3cca5ba136d8@gmail.com --- arch/x86/platform/geode/geode-common.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/arch/x86/platform/geode/geode-common.c b/arch/x86/platform/geode/geode-common.c index 05189c5f7d2a41..1843ae385e2dc0 100644 --- a/arch/x86/platform/geode/geode-common.c +++ b/arch/x86/platform/geode/geode-common.c @@ -28,8 +28,10 @@ static const struct software_node geode_gpio_keys_node = { .properties = geode_gpio_keys_props, }; -static struct property_entry geode_restart_key_props[] = { - { /* Placeholder for GPIO property */ }, +static struct software_node_ref_args geode_restart_gpio_ref; + +static const struct property_entry geode_restart_key_props[] = { + PROPERTY_ENTRY_REF_ARRAY_LEN("gpios", &geode_restart_gpio_ref, 1), PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), PROPERTY_ENTRY_STRING("label", "Reset button"), PROPERTY_ENTRY_U32("debounce-interval", 100), @@ -64,8 +66,7 @@ int __init geode_create_restart_key(unsigned int pin) struct platform_device *pd; int err; - geode_restart_key_props[0] = PROPERTY_ENTRY_GPIO("gpios", - &geode_gpiochip_node, + geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, pin, GPIO_ACTIVE_LOW); err = software_node_register_node_group(geode_gpio_keys_swnodes); @@ -99,6 +100,7 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, const struct software_node *group[MAX_LEDS + 2] = { 0 }; struct software_node *swnodes; struct property_entry *props; + struct software_node_ref_args *gpio_refs; struct platform_device_info led_info = { .name = "leds-gpio", .id = PLATFORM_DEVID_NONE, @@ -127,6 +129,12 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_swnodes; } + gpio_refs = kzalloc_objs(*gpio_refs, n_leds); + if (!gpio_refs) { + err = -ENOMEM; + goto err_free_props; + } + group[0] = &geode_gpio_leds_node; for (i = 0; i < n_leds; i++) { node_name = kasprintf(GFP_KERNEL, "%s:%d", label, i); @@ -135,9 +143,11 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_names; } + gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + leds[i].pin, + GPIO_ACTIVE_LOW); props[i * 3 + 0] = - PROPERTY_ENTRY_GPIO("gpios", &geode_gpiochip_node, - leds[i].pin, GPIO_ACTIVE_LOW); + PROPERTY_ENTRY_REF_ARRAY_LEN("gpios", &gpio_refs[i], 1); props[i * 3 + 1] = PROPERTY_ENTRY_STRING("linux,default-trigger", leds[i].default_on ? @@ -171,6 +181,8 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, err_free_names: while (--i >= 0) kfree(swnodes[i].name); + kfree(gpio_refs); +err_free_props: kfree(props); err_free_swnodes: kfree(swnodes); From af416cd9b3fb9d17ac7f4cfa12d1ea83dfd0e4be Mon Sep 17 00:00:00 2001 From: Jessica Liu Date: Tue, 31 Mar 2026 09:30:29 +0800 Subject: [PATCH 253/700] irqchip/riscv-aplic: Restrict genpd notifier to device tree only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ACPI systems, the aplic's pm_domain is set to acpi_general_pm_domain, which provides its own power management callbacks (e.g., runtime_suspend via acpi_subsys_runtime_suspend). aplic_pm_add() unconditionally calls dev_pm_genpd_add_notifier() when dev->pm_domain is non‑NULL, leading to a comparison between runtime_suspend and genpd_runtime_suspend. This results in the following errors when ACPI is enabled: riscv-aplic RSCV0002:00: failed to create APLIC context riscv-aplic RSCV0002:00: error -ENODEV: failed to setup APLIC in MSI mode Fix this by checking for dev->of_node before adding or removing the genpd notifier, ensuring it is only used for device tree based systems. Fixes: 95a8ddde3660 ("irqchip/riscv-aplic: Preserve APLIC states across suspend/resume") Signed-off-by: Jessica Liu Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260331093029749vRpdH-0qoEqjS0Wnn9M4x@zte.com.cn --- drivers/irqchip/irq-riscv-aplic-main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 9f53979b69625f..d9afb6ae98cf56 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -150,7 +150,7 @@ static void aplic_pm_remove(void *data) struct device *dev = priv->dev; list_del(&priv->head); - if (dev->pm_domain) + if (dev->pm_domain && dev->of_node) dev_pm_genpd_remove_notifier(dev); } @@ -165,7 +165,7 @@ static int aplic_pm_add(struct device *dev, struct aplic_priv *priv) priv->saved_hw_regs.srcs = srcs; list_add(&priv->head, &aplics); - if (dev->pm_domain) { + if (dev->pm_domain && dev->of_node) { priv->genpd_nb.notifier_call = aplic_pm_notifier; ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); if (ret) From a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 26 Mar 2026 15:30:20 +0900 Subject: [PATCH 254/700] crypto: algif_aead - Revert to operating out-of-place This mostly reverts commit 72548b093ee3 except for the copying of the associated data. There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings. Get rid of all the complexity added for in-place operation and just copy the AD directly. Fixes: 72548b093ee3 ("crypto: algif_aead - copy AAD from src to dst") Reported-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Herbert Xu --- crypto/af_alg.c | 49 ++++---------------- crypto/algif_aead.c | 100 ++++++++-------------------------------- crypto/algif_skcipher.c | 6 +-- include/crypto/if_alg.h | 5 +- 4 files changed, 34 insertions(+), 126 deletions(-) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index c2fd9cd86c5efa..8e0199394984d8 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -637,15 +637,13 @@ static int af_alg_alloc_tsgl(struct sock *sk) /** * af_alg_count_tsgl - Count number of TX SG entries * - * The counting starts from the beginning of the SGL to @bytes. If - * an @offset is provided, the counting of the SG entries starts at the @offset. + * The counting starts from the beginning of the SGL to @bytes. * * @sk: socket of connection to user space * @bytes: Count the number of SG entries holding given number of bytes. - * @offset: Start the counting of SG entries from the given offset. * Return: Number of TX SG entries found given the constraints */ -unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset) +unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes) { const struct alg_sock *ask = alg_sk(sk); const struct af_alg_ctx *ctx = ask->private; @@ -660,25 +658,11 @@ unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset) const struct scatterlist *sg = sgl->sg; for (i = 0; i < sgl->cur; i++) { - size_t bytes_count; - - /* Skip offset */ - if (offset >= sg[i].length) { - offset -= sg[i].length; - bytes -= sg[i].length; - continue; - } - - bytes_count = sg[i].length - offset; - - offset = 0; sgl_count++; - - /* If we have seen requested number of bytes, stop */ - if (bytes_count >= bytes) + if (sg[i].length >= bytes) return sgl_count; - bytes -= bytes_count; + bytes -= sg[i].length; } } @@ -690,19 +674,14 @@ EXPORT_SYMBOL_GPL(af_alg_count_tsgl); * af_alg_pull_tsgl - Release the specified buffers from TX SGL * * If @dst is non-null, reassign the pages to @dst. The caller must release - * the pages. If @dst_offset is given only reassign the pages to @dst starting - * at the @dst_offset (byte). The caller must ensure that @dst is large - * enough (e.g. by using af_alg_count_tsgl with the same offset). + * the pages. * * @sk: socket of connection to user space * @used: Number of bytes to pull from TX SGL * @dst: If non-NULL, buffer is reassigned to dst SGL instead of releasing. The * caller must release the buffers in dst. - * @dst_offset: Reassign the TX SGL from given offset. All buffers before - * reaching the offset is released. */ -void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, - size_t dst_offset) +void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst) { struct alg_sock *ask = alg_sk(sk); struct af_alg_ctx *ctx = ask->private; @@ -727,18 +706,10 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, * SG entries in dst. */ if (dst) { - if (dst_offset >= plen) { - /* discard page before offset */ - dst_offset -= plen; - } else { - /* reassign page to dst after offset */ - get_page(page); - sg_set_page(dst + j, page, - plen - dst_offset, - sg[i].offset + dst_offset); - dst_offset = 0; - j++; - } + /* reassign page to dst after offset */ + get_page(page); + sg_set_page(dst + j, page, plen, sg[i].offset); + j++; } sg[i].length -= plen; diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 79b016a899a1ef..dda15bb05e892d 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -72,9 +71,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, struct alg_sock *pask = alg_sk(psk); struct af_alg_ctx *ctx = ask->private; struct crypto_aead *tfm = pask->private; - unsigned int i, as = crypto_aead_authsize(tfm); + unsigned int as = crypto_aead_authsize(tfm); struct af_alg_async_req *areq; - struct af_alg_tsgl *tsgl, *tmp; struct scatterlist *rsgl_src, *tsgl_src = NULL; int err = 0; size_t used = 0; /* [in] TX bufs to be en/decrypted */ @@ -154,23 +152,24 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, outlen -= less; } + /* + * Create a per request TX SGL for this request which tracks the + * SG entries from the global TX SGL. + */ processed = used + ctx->aead_assoclen; - list_for_each_entry_safe(tsgl, tmp, &ctx->tsgl_list, list) { - for (i = 0; i < tsgl->cur; i++) { - struct scatterlist *process_sg = tsgl->sg + i; - - if (!(process_sg->length) || !sg_page(process_sg)) - continue; - tsgl_src = process_sg; - break; - } - if (tsgl_src) - break; - } - if (processed && !tsgl_src) { - err = -EFAULT; + areq->tsgl_entries = af_alg_count_tsgl(sk, processed); + if (!areq->tsgl_entries) + areq->tsgl_entries = 1; + areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), + areq->tsgl_entries), + GFP_KERNEL); + if (!areq->tsgl) { + err = -ENOMEM; goto free; } + sg_init_table(areq->tsgl, areq->tsgl_entries); + af_alg_pull_tsgl(sk, processed, areq->tsgl); + tsgl_src = areq->tsgl; /* * Copy of AAD from source to destination @@ -179,76 +178,15 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, * when user space uses an in-place cipher operation, the kernel * will copy the data as it does not see whether such in-place operation * is initiated. - * - * To ensure efficiency, the following implementation ensure that the - * ciphers are invoked to perform a crypto operation in-place. This - * is achieved by memory management specified as follows. */ /* Use the RX SGL as source (and destination) for crypto op. */ rsgl_src = areq->first_rsgl.sgl.sgt.sgl; - if (ctx->enc) { - /* - * Encryption operation - The in-place cipher operation is - * achieved by the following operation: - * - * TX SGL: AAD || PT - * | | - * | copy | - * v v - * RX SGL: AAD || PT || Tag - */ - memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src, - processed); - af_alg_pull_tsgl(sk, processed, NULL, 0); - } else { - /* - * Decryption operation - To achieve an in-place cipher - * operation, the following SGL structure is used: - * - * TX SGL: AAD || CT || Tag - * | | ^ - * | copy | | Create SGL link. - * v v | - * RX SGL: AAD || CT ----+ - */ - - /* Copy AAD || CT to RX SGL buffer for in-place operation. */ - memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src, outlen); - - /* Create TX SGL for tag and chain it to RX SGL. */ - areq->tsgl_entries = af_alg_count_tsgl(sk, processed, - processed - as); - if (!areq->tsgl_entries) - areq->tsgl_entries = 1; - areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), - areq->tsgl_entries), - GFP_KERNEL); - if (!areq->tsgl) { - err = -ENOMEM; - goto free; - } - sg_init_table(areq->tsgl, areq->tsgl_entries); - - /* Release TX SGL, except for tag data and reassign tag data. */ - af_alg_pull_tsgl(sk, processed, areq->tsgl, processed - as); - - /* chain the areq TX SGL holding the tag with RX SGL */ - if (usedpages) { - /* RX SGL present */ - struct af_alg_sgl *sgl_prev = &areq->last_rsgl->sgl; - struct scatterlist *sg = sgl_prev->sgt.sgl; - - sg_unmark_end(sg + sgl_prev->sgt.nents - 1); - sg_chain(sg, sgl_prev->sgt.nents + 1, areq->tsgl); - } else - /* no RX SGL present (e.g. authentication only) */ - rsgl_src = areq->tsgl; - } + memcpy_sglist(rsgl_src, tsgl_src, ctx->aead_assoclen); /* Initialize the crypto operation */ - aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src, + aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, areq->first_rsgl.sgl.sgt.sgl, used, ctx->iv); aead_request_set_ad(&areq->cra_u.aead_req, ctx->aead_assoclen); aead_request_set_tfm(&areq->cra_u.aead_req, tfm); @@ -450,7 +388,7 @@ static void aead_sock_destruct(struct sock *sk) struct crypto_aead *tfm = pask->private; unsigned int ivlen = crypto_aead_ivsize(tfm); - af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + af_alg_pull_tsgl(sk, ctx->used, NULL); sock_kzfree_s(sk, ctx->iv, ivlen); sock_kfree_s(sk, ctx, ctx->len); af_alg_release_parent(sk); diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 125d395c5e009e..82735e51be108d 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -138,7 +138,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, * Create a per request TX SGL for this request which tracks the * SG entries from the global TX SGL. */ - areq->tsgl_entries = af_alg_count_tsgl(sk, len, 0); + areq->tsgl_entries = af_alg_count_tsgl(sk, len); if (!areq->tsgl_entries) areq->tsgl_entries = 1; areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), @@ -149,7 +149,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, goto free; } sg_init_table(areq->tsgl, areq->tsgl_entries); - af_alg_pull_tsgl(sk, len, areq->tsgl, 0); + af_alg_pull_tsgl(sk, len, areq->tsgl); /* Initialize the crypto operation */ skcipher_request_set_tfm(&areq->cra_u.skcipher_req, tfm); @@ -363,7 +363,7 @@ static void skcipher_sock_destruct(struct sock *sk) struct alg_sock *pask = alg_sk(psk); struct crypto_skcipher *tfm = pask->private; - af_alg_pull_tsgl(sk, ctx->used, NULL, 0); + af_alg_pull_tsgl(sk, ctx->used, NULL); sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm)); if (ctx->state) sock_kzfree_s(sk, ctx->state, crypto_skcipher_statesize(tfm)); diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 107b797c33ecf7..0cc8fa749f68d2 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -230,9 +230,8 @@ static inline bool af_alg_readable(struct sock *sk) return PAGE_SIZE <= af_alg_rcvbuf(sk); } -unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset); -void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst, - size_t dst_offset); +unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes); +void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst); void af_alg_wmem_wakeup(struct sock *sk); int af_alg_wait_for_data(struct sock *sk, unsigned flags, unsigned min); int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, From e02494114ebf7c8b42777c6cd6982f113bfdbec7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 27 Mar 2026 15:04:17 +0900 Subject: [PATCH 255/700] crypto: authencesn - Do not place hiseq at end of dst for out-of-place decryption When decrypting data that is not in-place (src != dst), there is no need to save the high-order sequence bits in dst as it could simply be re-copied from the source. However, the data to be hashed need to be rearranged accordingly. Reported-by: Taeyang Lee <0wn@theori.io> Fixes: 104880a6b470 ("crypto: authencesn - Convert to new AEAD interface") Signed-off-by: Herbert Xu Thanks, Signed-off-by: Herbert Xu --- crypto/authencesn.c | 48 +++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/crypto/authencesn.c b/crypto/authencesn.c index 542a978663b9e7..c0a01d738d9bc3 100644 --- a/crypto/authencesn.c +++ b/crypto/authencesn.c @@ -207,6 +207,7 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, u8 *ohash = areq_ctx->tail; unsigned int cryptlen = req->cryptlen - authsize; unsigned int assoclen = req->assoclen; + struct scatterlist *src = req->src; struct scatterlist *dst = req->dst; u8 *ihash = ohash + crypto_ahash_digestsize(auth); u32 tmp[2]; @@ -214,23 +215,27 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, if (!authsize) goto decrypt; - /* Move high-order bits of sequence number back. */ - scatterwalk_map_and_copy(tmp, dst, 4, 4, 0); - scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0); - scatterwalk_map_and_copy(tmp, dst, 0, 8, 1); + if (src == dst) { + /* Move high-order bits of sequence number back. */ + scatterwalk_map_and_copy(tmp, dst, 4, 4, 0); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0); + scatterwalk_map_and_copy(tmp, dst, 0, 8, 1); + } else + memcpy_sglist(dst, src, assoclen); if (crypto_memneq(ihash, ohash, authsize)) return -EBADMSG; decrypt: - sg_init_table(areq_ctx->dst, 2); + if (src != dst) + src = scatterwalk_ffwd(areq_ctx->src, src, assoclen); dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen); skcipher_request_set_tfm(skreq, ctx->enc); skcipher_request_set_callback(skreq, flags, req->base.complete, req->base.data); - skcipher_request_set_crypt(skreq, dst, dst, cryptlen, req->iv); + skcipher_request_set_crypt(skreq, src, dst, cryptlen, req->iv); return crypto_skcipher_decrypt(skreq); } @@ -255,6 +260,7 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req) unsigned int assoclen = req->assoclen; unsigned int cryptlen = req->cryptlen; u8 *ihash = ohash + crypto_ahash_digestsize(auth); + struct scatterlist *src = req->src; struct scatterlist *dst = req->dst; u32 tmp[2]; int err; @@ -262,24 +268,28 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req) if (assoclen < 8) return -EINVAL; - cryptlen -= authsize; - - if (req->src != dst) - memcpy_sglist(dst, req->src, assoclen + cryptlen); + if (!authsize) + goto tail; + cryptlen -= authsize; scatterwalk_map_and_copy(ihash, req->src, assoclen + cryptlen, authsize, 0); - if (!authsize) - goto tail; - /* Move high-order bits of sequence number to the end. */ - scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); - scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); - scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); - - sg_init_table(areq_ctx->dst, 2); - dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + scatterwalk_map_and_copy(tmp, src, 0, 8, 0); + if (src == dst) { + scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); + dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + } else { + scatterwalk_map_and_copy(tmp, dst, 0, 4, 1); + scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen - 4, 4, 1); + + src = scatterwalk_ffwd(areq_ctx->src, src, 8); + dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4); + memcpy_sglist(dst, src, assoclen + cryptlen - 8); + dst = req->dst; + } ahash_request_set_tfm(ahreq, auth); ahash_request_set_crypt(ahreq, dst, ohash, assoclen + cryptlen); From 2884bf72fb8f03409e423397319205de48adca16 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 26 Mar 2026 00:55:53 -0700 Subject: [PATCH 256/700] net: bonding: fix use-after-free in bond_xmit_broadcast() bond_xmit_broadcast() reuses the original skb for the last slave (determined by bond_is_last_slave()) and clones it for others. Concurrent slave enslave/release can mutate the slave list during RCU-protected iteration, changing which slave is "last" mid-loop. This causes the original skb to be double-consumed (double-freed). Replace the racy bond_is_last_slave() check with a simple index comparison (i + 1 == slaves_count) against the pre-snapshot slave count taken via READ_ONCE() before the loop. This preserves the zero-copy optimization for the last slave while making the "last" determination stable against concurrent list mutations. The UAF can trigger the following crash: ================================================================== BUG: KASAN: slab-use-after-free in skb_clone Read of size 8 at addr ffff888100ef8d40 by task exploit/147 CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY Call Trace: dump_stack_lvl (lib/dump_stack.c:123) print_report (mm/kasan/report.c:379 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:597) skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108) bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334) bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593) dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887) __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838) ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136) ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219) ip6_output (net/ipv6/ip6_output.c:250) ip6_send_skb (net/ipv6/ip6_output.c:1985) udp_v6_send_skb (net/ipv6/udp.c:1442) udpv6_sendmsg (net/ipv6/udp.c:1733) __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206) __x64_sys_sendto (net/socket.c:2209) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Allocated by task 147: Freed by task 147: The buggy address belongs to the object at ffff888100ef8c80 which belongs to the cache skbuff_head_cache of size 224 The buggy address is located 192 bytes inside of freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60) Memory state around the buggy address: ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc ^ ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: 4e5bd03ae346 ("net: bonding: fix bond_xmit_broadcast return value error bug") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260326075553.3960562-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 33f414d03ab913..a5484d11553d1a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5326,7 +5326,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb, if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)) continue; - if (bond_is_last_slave(bond, slave)) { + if (i + 1 == slaves_count) { skb2 = skb; skb_used = true; } else { From 30fe3f5f6494f827d812ff179f295a8e532709d6 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 26 Mar 2026 22:20:33 +0800 Subject: [PATCH 257/700] NFC: pn533: bound the UART receive buffer pn532_receive_buf() appends every incoming byte to dev->recv_skb and only resets the buffer after pn532_uart_rx_is_frame() recognizes a complete frame. A continuous stream of bytes without a valid PN532 frame header therefore keeps growing the skb until skb_put_u8() hits the tail limit. Drop the accumulated partial frame once the fixed receive buffer is full so malformed UART traffic cannot grow the skb past PN532_UART_SKB_BUFF_LEN. Fixes: c656aa4c27b1 ("nfc: pn533: add UART phy driver") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260326142033.82297-1-pengpeng@iscas.ac.cn Signed-off-by: Paolo Abeni --- drivers/nfc/pn533/uart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index 6d2f520a5bc8d0..1b82b7b2a5fae8 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -211,6 +211,9 @@ static size_t pn532_receive_buf(struct serdev_device *serdev, timer_delete(&dev->cmd_timeout); for (i = 0; i < count; i++) { + if (unlikely(!skb_tailroom(dev->recv_skb))) + skb_trim(dev->recv_skb, 0); + skb_put_u8(dev->recv_skb, *data++); if (!pn532_uart_rx_is_frame(dev->recv_skb)) continue; From 1635c2acdde86c4f555b627aec873c8677c421ed Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 27 Mar 2026 09:18:21 +1100 Subject: [PATCH 258/700] cachefiles: fix incorrect dentry refcount in cachefiles_cull() The patch mentioned below changed cachefiles_bury_object() to expect 2 references to the 'rep' dentry. Three of the callers were changed to use start_removing_dentry() which takes an extra reference so in those cases the call gets the expected references. However there is another call to cachefiles_bury_object() in cachefiles_cull() which did not need to be changed to use start_removing_dentry() and so was not properly considered. It still passed the dentry with just one reference so the net result is that a reference is lost. To meet the expectations of cachefiles_bury_object(), cachefiles_cull() must take an extra reference before the call. It will be dropped by cachefiles_bury_object(). Reported-by: Marc Dionne Fixes: 7bb1eb45e43c ("VFS: introduce start_removing_dentry()") Signed-off-by: NeilBrown Link: https://patch.msgid.link/177456350181.1851489.16359967086642190170@noble.neil.brown.name Acked-by: Paulo Alcantara (Red Hat) Signed-off-by: Christian Brauner --- fs/cachefiles/namei.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index e5ec90dccc27f7..eb9eb7683e3ccf 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -810,6 +810,11 @@ int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, if (ret < 0) goto error_unlock; + /* + * cachefiles_bury_object() expects 2 references to 'victim', + * and drops one. + */ + dget(victim); ret = cachefiles_bury_object(cache, NULL, dir, victim, FSCACHE_OBJECT_WAS_CULLED); dput(victim); From 393e0b4f178ec7fce1141dacc3304e3607a92ee9 Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Fri, 27 Mar 2026 13:02:37 +0530 Subject: [PATCH 259/700] net: xilinx: axienet: Correct BD length masks to match AXIDMA IP spec The XAXIDMA_BD_CTRL_LENGTH_MASK and XAXIDMA_BD_STS_ACTUAL_LEN_MASK macros were defined as 0x007FFFFF (23 bits), but the AXI DMA IP product guide (PG021) specifies the buffer length field as bits 25:0 (26 bits). Update both masks to match the IP documentation. In practice this had no functional impact, since Ethernet frames are far smaller than 2^23 bytes and the extra bits were always zero, but the masks should still reflect the hardware specification. Fixes: 8a3b7a252dca ("drivers/net/ethernet/xilinx: added Xilinx AXI Ethernet driver") Signed-off-by: Suraj Gupta Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260327073238.134948-2-suraj.gupta2@amd.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/xilinx/xilinx_axienet.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 5ff742103beb99..fcd3aaef27fc32 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -105,7 +105,7 @@ #define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */ #define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */ -#define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */ +#define XAXIDMA_BD_CTRL_LENGTH_MASK GENMASK(25, 0) /* Requested len */ #define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */ #define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ #define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */ @@ -130,7 +130,7 @@ #define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */ #define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */ -#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */ +#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK GENMASK(25, 0) /* Actual len */ #define XAXIDMA_BD_STS_COMPLETE_MASK 0x80000000 /* Completed */ #define XAXIDMA_BD_STS_DEC_ERR_MASK 0x40000000 /* Decode error */ #define XAXIDMA_BD_STS_SLV_ERR_MASK 0x20000000 /* Slave error */ From d1978d03e86785872871bff9c2623174b10740de Mon Sep 17 00:00:00 2001 From: Suraj Gupta Date: Fri, 27 Mar 2026 13:02:38 +0530 Subject: [PATCH 260/700] net: xilinx: axienet: Fix BQL accounting for multi-BD TX packets When a TX packet spans multiple buffer descriptors (scatter-gather), axienet_free_tx_chain sums the per-BD actual length from descriptor status into a caller-provided accumulator. That sum is reset on each NAPI poll. If the BDs for a single packet complete across different polls, the earlier bytes are lost and never credited to BQL. This causes BQL to think bytes are permanently in-flight, eventually stalling the TX queue. The SKB pointer is stored only on the last BD of a packet. When that BD completes, use skb->len for the byte count instead of summing per-BD status lengths. This matches netdev_sent_queue(), which debits skb->len, and naturally survives across polls because no partial packet contributes to the accumulator. Fixes: c900e49d58eb ("net: xilinx: axienet: Implement BQL") Signed-off-by: Suraj Gupta Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260327073238.134948-3-suraj.gupta2@amd.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index b06e4c37ff617e..263c4b67fd5a29 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -770,8 +770,8 @@ static int axienet_device_reset(struct net_device *ndev) * @first_bd: Index of first descriptor to clean up * @nr_bds: Max number of descriptors to clean up * @force: Whether to clean descriptors even if not complete - * @sizep: Pointer to a u32 filled with the total sum of all bytes - * in all cleaned-up descriptors. Ignored if NULL. + * @sizep: Pointer to a u32 accumulating the total byte count of + * completed packets (using skb->len). Ignored if NULL. * @budget: NAPI budget (use 0 when not called from NAPI poll) * * Would either be called after a successful transmit operation, or after @@ -805,6 +805,8 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, DMA_TO_DEVICE); if (cur_p->skb && (status & XAXIDMA_BD_STS_COMPLETE_MASK)) { + if (sizep) + *sizep += cur_p->skb->len; napi_consume_skb(cur_p->skb, budget); packets++; } @@ -818,9 +820,6 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, wmb(); cur_p->cntrl = 0; cur_p->status = 0; - - if (sizep) - *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK; } if (!force) { From 5e77923a3eb39cce91bf08ed7670f816bf86d4af Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Wed, 11 Mar 2026 14:46:52 +0800 Subject: [PATCH 261/700] drm/sysfb: Fix efidrm error handling and memory type mismatch Fix incorrect error checking and memory type confusion in efidrm_device_create(). devm_memremap() returns error pointers, not NULL, and returns system memory while devm_ioremap() returns I/O memory. The code incorrectly passes system memory to iosys_map_set_vaddr_iomem(). Restructure to handle each memory type separately. Use devm_ioremap*() with ERR_PTR(-ENXIO) for WC/UC, and devm_memremap() with ERR_CAST() for WT/WB. Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Signed-off-by: Chen Ni Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260311064652.2903449-1-nichen@iscas.ac.cn --- drivers/gpu/drm/sysfb/efidrm.c | 46 +++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c index 50e0aeef709cb1..9d84caf6962767 100644 --- a/drivers/gpu/drm/sysfb/efidrm.c +++ b/drivers/gpu/drm/sysfb/efidrm.c @@ -151,7 +151,6 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, struct drm_sysfb_device *sysfb; struct drm_device *dev; struct resource *mem = NULL; - void __iomem *screen_base = NULL; struct drm_plane *primary_plane; struct drm_crtc *crtc; struct drm_encoder *encoder; @@ -238,21 +237,38 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, mem_flags = efidrm_get_mem_flags(dev, res->start, vsize); - if (mem_flags & EFI_MEMORY_WC) - screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); - else if (mem_flags & EFI_MEMORY_UC) - screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - else if (mem_flags & EFI_MEMORY_WT) - screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem), - MEMREMAP_WT); - else if (mem_flags & EFI_MEMORY_WB) - screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem), - MEMREMAP_WB); - else + if (mem_flags & EFI_MEMORY_WC) { + void __iomem *screen_base = devm_ioremap_wc(&pdev->dev, mem->start, + resource_size(mem)); + + if (!screen_base) + return ERR_PTR(-ENXIO); + iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_UC) { + void __iomem *screen_base = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); + + if (!screen_base) + return ERR_PTR(-ENXIO); + iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_WT) { + void *screen_base = devm_memremap(&pdev->dev, mem->start, + resource_size(mem), MEMREMAP_WT); + + if (IS_ERR(screen_base)) + return ERR_CAST(screen_base); + iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); + } else if (mem_flags & EFI_MEMORY_WB) { + void *screen_base = devm_memremap(&pdev->dev, mem->start, + resource_size(mem), MEMREMAP_WB); + + if (IS_ERR(screen_base)) + return ERR_CAST(screen_base); + iosys_map_set_vaddr(&sysfb->fb_addr, screen_base); + } else { drm_err(dev, "invalid mem_flags: 0x%llx\n", mem_flags); - if (!screen_base) - return ERR_PTR(-ENOMEM); - iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + return ERR_PTR(-EINVAL); + } /* * Modesetting From dff34ef879c5e73298443956a8b391311ba78d57 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2026 11:52:05 +0100 Subject: [PATCH 262/700] mmc: vub300: fix NULL-deref on disconnect Make sure to deregister the controller before dropping the reference to the driver data on disconnect to avoid NULL-pointer dereferences or use-after-free. Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver") Cc: stable@vger.kernel.org # 3.0+ Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index ff49d0770506f7..f173c7cf4e1a47 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2365,8 +2365,8 @@ static void vub300_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); /* prevent more I/O from starting */ vub300->interface = NULL; - kref_put(&vub300->kref, vub300_delete); mmc_remove_host(mmc); + kref_put(&vub300->kref, vub300_delete); pr_info("USB vub300 remote SDIO host controller[%d]" " now disconnected", ifnum); return; From 8f4d20a710225ec7a565f6a0459862d3b1f32330 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2026 11:52:06 +0100 Subject: [PATCH 263/700] mmc: vub300: fix use-after-free on disconnect The vub300 driver maintains an explicit reference count for the controller and its driver data and the last reference can in theory be dropped after the driver has been unbound. This specifically means that the controller allocation must not be device managed as that can lead to use-after-free. Note that the lifetime is currently also incorrectly tied the parent USB device rather than interface, which can lead to memory leaks if the driver is unbound without its device being physically disconnected (e.g. on probe deferral). Fix both issues by reverting to non-managed allocation of the controller. Fixes: dcfdd698dc52 ("mmc: vub300: Use devm_mmc_alloc_host() helper") Cc: stable@vger.kernel.org # 6.17+ Cc: Binbin Zhou Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index f173c7cf4e1a47..3c9df27f9fa74d 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -369,11 +369,14 @@ struct vub300_mmc_host { static void vub300_delete(struct kref *kref) { /* kref callback - softirq */ struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref); + struct mmc_host *mmc = vub300->mmc; + usb_free_urb(vub300->command_out_urb); vub300->command_out_urb = NULL; usb_free_urb(vub300->command_res_urb); vub300->command_res_urb = NULL; usb_put_dev(vub300->udev); + mmc_free_host(mmc); /* * and hence also frees vub300 * which is contained at the end of struct mmc @@ -2112,7 +2115,7 @@ static int vub300_probe(struct usb_interface *interface, goto error1; } /* this also allocates memory for our VUB300 mmc host device */ - mmc = devm_mmc_alloc_host(&udev->dev, sizeof(*vub300)); + mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); @@ -2269,7 +2272,7 @@ static int vub300_probe(struct usb_interface *interface, dev_err(&vub300->udev->dev, "Could not find two sets of bulk-in/out endpoint pairs\n"); retval = -EINVAL; - goto error4; + goto err_free_host; } retval = usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), @@ -2278,14 +2281,14 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->hc_info, sizeof(vub300->hc_info), 1000); if (retval < 0) - goto error4; + goto err_free_host; retval = usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), SET_ROM_WAIT_STATES, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, firmware_rom_wait_states, 0x0000, NULL, 0, 1000); if (retval < 0) - goto error4; + goto err_free_host; dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n", (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL", @@ -2300,7 +2303,7 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (retval < 0) { - goto error4; + goto err_free_host; } else if (sizeof(vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; @@ -2308,7 +2311,7 @@ static int vub300_probe(struct usb_interface *interface, (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; } else { retval = -EINVAL; - goto error4; + goto err_free_host; } usb_set_intfdata(interface, vub300); INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); @@ -2338,6 +2341,8 @@ static int vub300_probe(struct usb_interface *interface, return 0; error6: timer_delete_sync(&vub300->inactivity_timer); +err_free_host: + mmc_free_host(mmc); /* * and hence also frees vub300 * which is contained at the end of struct mmc From 45b859b0728267a6199ee5002d62e6c6f3e8c89d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 27 Mar 2026 10:49:52 +0100 Subject: [PATCH 264/700] thermal: core: Address thermal zone removal races with resume Since thermal_zone_pm_complete() and thermal_zone_device_resume() re-initialize the poll_queue delayed work for the given thermal zone, the cancel_delayed_work_sync() in thermal_zone_device_unregister() may miss some already running work items and the thermal zone may be freed prematurely [1]. There are two failing scenarios that both start with running thermal_pm_notify_complete() right before invoking thermal_zone_device_unregister() for one of the thermal zones. In the first scenario, there is a work item already running for the given thermal zone when thermal_pm_notify_complete() calls thermal_zone_pm_complete() for that thermal zone and it continues to run when thermal_zone_device_unregister() starts. Since the poll_queue delayed work has been re-initialized by thermal_pm_notify_complete(), the running work item will be missed by the cancel_delayed_work_sync() in thermal_zone_device_unregister() and if it continues to run past the freeing of the thermal zone object, a use-after-free will occur. In the second scenario, thermal_zone_device_resume() queued up by thermal_pm_notify_complete() runs right after the thermal_zone_exit() called by thermal_zone_device_unregister() has returned. The poll_queue delayed work is re-initialized by it before cancel_delayed_work_sync() is called by thermal_zone_device_unregister(), so it may continue to run after the freeing of the thermal zone object, which also leads to a use-after-free. Address the first failing scenario by ensuring that no thermal work items will be running when thermal_pm_notify_complete() is called. For this purpose, first move the cancel_delayed_work() call from thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent new work from entering the workqueue going forward. Next, switch over to using a dedicated workqueue for thermal events and update the code in thermal_pm_notify() to flush that workqueue after thermal_pm_notify_prepare() has returned which will take care of all leftover thermal work already on the workqueue (that leftover work would do nothing useful anyway because all of the thermal zones have been flagged as suspended). The second failing scenario is addressed by adding a tz->state check to thermal_zone_device_resume() to prevent it from re-initializing the poll_queue delayed work if the thermal zone is going away. Note that the above changes will also facilitate relocating the suspend and resume of thermal zones closer to the suspend and resume of devices, respectively. Fixes: 5a5efdaffda5 ("thermal: core: Resume thermal zones asynchronously") Reported-by: syzbot+3b3852c6031d0f30dfaf@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=3b3852c6031d0f30dfaf Reported-by: Mauricio Faria de Oliveira Closes: https://lore.kernel.org/linux-pm/20260324-thermal-core-uaf-init_delayed_work-v1-1-6611ae76a8a1@igalia.com/ [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Mauricio Faria de Oliveira Tested-by: Mauricio Faria de Oliveira Reviewed-by: Lukasz Luba Cc: All applicable Link: https://patch.msgid.link/6267615.lOV4Wx5bFT@rafael.j.wysocki --- drivers/thermal/thermal_core.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b7d706ed7ed96a..5337612e484da6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -41,6 +41,8 @@ static struct thermal_governor *def_governor; static bool thermal_pm_suspended; +static struct workqueue_struct *thermal_wq __ro_after_init; + /* * Governor section: set of functions to handle thermal governors * @@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, if (delay > HZ) delay = round_jiffies_relative(delay); - mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); + mod_delayed_work(thermal_wq, &tz->poll_queue, delay); } static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) @@ -1785,6 +1787,10 @@ static void thermal_zone_device_resume(struct work_struct *work) guard(thermal_zone)(tz); + /* If the thermal zone is going away, there's nothing to do. */ + if (tz->state & TZ_STATE_FLAG_EXIT) + return; + tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); thermal_debug_tz_resume(tz); @@ -1811,6 +1817,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) } tz->state |= TZ_STATE_FLAG_SUSPENDED; + + /* Prevent new work from getting to the workqueue subsequently. */ + cancel_delayed_work(&tz->poll_queue); } static void thermal_pm_notify_prepare(void) @@ -1829,8 +1838,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) { guard(thermal_zone)(tz); - cancel_delayed_work(&tz->poll_queue); - reinit_completion(&tz->resume); tz->state |= TZ_STATE_FLAG_RESUMING; @@ -1840,7 +1847,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz) */ INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); /* Queue up the work without a delay. */ - mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); + mod_delayed_work(thermal_wq, &tz->poll_queue, 0); } static void thermal_pm_notify_complete(void) @@ -1863,6 +1870,11 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_RESTORE_PREPARE: case PM_SUSPEND_PREPARE: thermal_pm_notify_prepare(); + /* + * Allow any leftover thermal work items already on the + * worqueue to complete so they don't get in the way later. + */ + flush_workqueue(thermal_wq); break; case PM_POST_HIBERNATION: case PM_POST_RESTORE: @@ -1895,9 +1907,16 @@ static int __init thermal_init(void) if (result) goto error; + thermal_wq = alloc_workqueue("thermal_events", + WQ_FREEZABLE | WQ_POWER_EFFICIENT | WQ_PERCPU, 0); + if (!thermal_wq) { + result = -ENOMEM; + goto unregister_netlink; + } + result = thermal_register_governors(); if (result) - goto unregister_netlink; + goto destroy_workqueue; thermal_class = kzalloc_obj(*thermal_class); if (!thermal_class) { @@ -1924,6 +1943,8 @@ static int __init thermal_init(void) unregister_governors: thermal_unregister_governors(); +destroy_workqueue: + destroy_workqueue(thermal_wq); unregister_netlink: thermal_netlink_exit(); error: From ec327abae5edd1d5b60ea9f920212970133171d2 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 14 Mar 2026 11:19:51 +0000 Subject: [PATCH 265/700] rust_binder: use AssertSync for BINDER_VM_OPS When declaring an immutable global variable in Rust, the compiler checks that it looks thread safe, because it is generally safe to access said global variable. When using C bindings types for these globals, we don't really want this check, because it is conservative and assumes pointers are not thread safe. In the case of BINDER_VM_OPS, this is a challenge when combined with the patch 'userfaultfd: introduce vm_uffd_ops' [1], which introduces a pointer field to vm_operations_struct. It previously only held function pointers, which are considered thread safe. Rust Binder should not be assuming that vm_operations_struct contains no pointer fields, so to fix this, use AssertSync (which Rust Binder has already declared for another similar global of type struct file_operations with the same problem). This ensures that even if another commit adds a pointer field to vm_operations_struct, this does not cause problems. Fixes: 8ef2c15aeae0 ("rust_binder: check ownership before using vma") Cc: stable Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603121235.tpnRxFKO-lkp@intel.com/ Link: https://lore.kernel.org/r/20260306171815.3160826-8-rppt@kernel.org [1] Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260314111951.4139029-1-aliceryhl@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/page_range.rs | 8 +++++--- drivers/android/binder/rust_binder_main.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index 9dfc154e5dd4e8..b57e0c7ba3f1f8 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -13,6 +13,8 @@ // // The shrinker will use trylock methods because it locks them in a different order. +use crate::AssertSync; + use core::{ marker::PhantomPinned, mem::{size_of, size_of_val, MaybeUninit}, @@ -143,14 +145,14 @@ pub(crate) struct ShrinkablePageRange { } // We do not define any ops. For now, used only to check identity of vmas. -static BINDER_VM_OPS: bindings::vm_operations_struct = pin_init::zeroed(); +static BINDER_VM_OPS: AssertSync = AssertSync(pin_init::zeroed()); // To ensure that we do not accidentally install pages into or zap pages from the wrong vma, we // check its vm_ops and private data before using it. fn check_vma(vma: &virt::VmaRef, owner: *const ShrinkablePageRange) -> Option<&virt::VmaMixedMap> { // SAFETY: Just reading the vm_ops pointer of any active vma is safe. let vm_ops = unsafe { (*vma.as_ptr()).vm_ops }; - if !ptr::eq(vm_ops, &BINDER_VM_OPS) { + if !ptr::eq(vm_ops, &BINDER_VM_OPS.0) { return None; } @@ -342,7 +344,7 @@ impl ShrinkablePageRange { // SAFETY: We own the vma, and we don't use any methods on VmaNew that rely on // `vm_ops`. - unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS }; + unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS.0 }; Ok(num_pages) } diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index aa5f2a75adb47e..014010662df82e 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -306,7 +306,7 @@ impl kernel::Module for BinderModule { /// Makes the inner type Sync. #[repr(transparent)] pub struct AssertSync(T); -// SAFETY: Used only to insert `file_operations` into a global, which is safe. +// SAFETY: Used only to insert C bindings types into globals, which is safe. unsafe impl Sync for AssertSync {} /// File operations that rust_binderfs.c can use. From 8c27b1bce059a11a8d3c8682984e13866f0714af Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 30 Mar 2026 11:38:30 +0300 Subject: [PATCH 266/700] mei: me: reduce the scope on unexpected reset After commit 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected") some devices started to show long resume times (5-7 seconds). This happens as mei falsely detects unready hardware, starts parallel link reset flow and triggers link reset timeouts in the resume callback. Address it by performing detection of unready hardware only when driver is in the MEI_DEV_ENABLED state instead of blacklisting states as done in the original patch. This eliminates active waitqueue check as in MEI_DEV_ENABLED state there will be no active waitqueue. Reviewed-by: Rafael J. Wysocki (Intel) Reported-by: Todd Brandt Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221023 Tested-by: Todd Brandt Fixes: 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected") Cc: stable Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260330083830.536056-1-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index d4612c6597844f..1e4a41ac428fd8 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1337,19 +1337,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { - /* synchronized by dev mutex */ - if (waitqueue_active(&dev->wait_hw_ready)) { - dev_dbg(&dev->dev, "we need to start the dev.\n"); - dev->recvd_hw_ready = true; - wake_up(&dev->wait_hw_ready); - } else if (dev->dev_state != MEI_DEV_UNINITIALIZED && - dev->dev_state != MEI_DEV_POWERING_DOWN && - dev->dev_state != MEI_DEV_POWER_DOWN) { + if (dev->dev_state == MEI_DEV_ENABLED) { dev_dbg(&dev->dev, "Force link reset.\n"); schedule_work(&dev->reset_work); } else { - dev_dbg(&dev->dev, "Ignore this interrupt in state = %d\n", - dev->dev_state); + dev_dbg(&dev->dev, "we need to start the dev.\n"); + dev->recvd_hw_ready = true; + wake_up(&dev->wait_hw_ready); } } else { dev_dbg(&dev->dev, "Spurious Interrupt\n"); From 69335152910b775e7835939d5c863c580c605275 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 27 Jan 2026 20:11:05 +0100 Subject: [PATCH 267/700] misc/mei: INTEL_MEI should depend on X86 or DRM_XE The Intel Management Engine Interface is only present on x86 platforms and Intel Xe graphics cards. Hence add a dependency on X86 or DRM_XE, to prevent asking the user about this driver when configuring a kernel for a non-x86 architecture and without Xe graphics support. Fixes: 25f9b0d35155 ("misc/mei: Allow building Intel ME interface on non-x86") Cc: stable Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/8e2646fb71b148b3d38beb13f19b14e3634a1e1a.1769541024.git.geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 5902dd1ee44baf..094fb1dde0fe80 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -3,6 +3,7 @@ config INTEL_MEI tristate "Intel Management Engine Interface" depends on PCI + depends on X86 || DRM_XE!=n || COMPILE_TEST default X86_64 || MATOM help The Intel Management Engine (Intel ME) provides Manageability, From fa6e24963342de4370e3a3c9af41e38277b74cf3 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Fri, 27 Mar 2026 23:30:00 -0700 Subject: [PATCH 268/700] bridge: mrp: reject zero test interval to avoid OOM panic br_mrp_start_test() and br_mrp_start_in_test() accept the user-supplied interval value from netlink without validation. When interval is 0, usecs_to_jiffies(0) yields 0, causing the delayed work (br_mrp_test_work_expired / br_mrp_in_test_work_expired) to reschedule itself with zero delay. This creates a tight loop on system_percpu_wq that allocates and transmits MRP test frames at maximum rate, exhausting all system memory and causing a kernel panic via OOM deadlock. The same zero-interval issue applies to br_mrp_start_in_test_parse() for interconnect test frames. Use NLA_POLICY_MIN(NLA_U32, 1) in the nla_policy tables for both IFLA_BRIDGE_MRP_START_TEST_INTERVAL and IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, so zero is rejected at the netlink attribute parsing layer before the value ever reaches the workqueue scheduling code. This is consistent with how other bridge subsystems (br_fdb, br_mst) enforce range constraints on netlink attributes. Fixes: 20f6a05ef635 ("bridge: mrp: Rework the MRP netlink interface") Fixes: 7ab1748e4ce6 ("bridge: mrp: Extend MRP netlink interface for configuring MRP interconnect") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260328063000.1845376-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/bridge/br_mrp_netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c index ce6f63c77cc0ac..86f0e75d6e345f 100644 --- a/net/bridge/br_mrp_netlink.c +++ b/net/bridge/br_mrp_netlink.c @@ -196,7 +196,7 @@ static const struct nla_policy br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 }, @@ -316,7 +316,7 @@ static const struct nla_policy br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NLA_U32 }, }; From 82cbae59263e3941a8bb79f0b25e0b0f085132d4 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Sun, 29 Mar 2026 14:46:57 +0200 Subject: [PATCH 269/700] platform/x86: asus-armoury: add support for GV302XU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GV302XU. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260329124659.3967495-2-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 569743746347cd..a53204c24d4370 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1376,6 +1376,33 @@ static const struct dmi_system_id power_limits[] = { }, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GV302XU"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 55, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 60, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GV302XV"), From b4f04a6038fa97f7ceea33e99456b9838c79dde7 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Sun, 29 Mar 2026 14:46:58 +0200 Subject: [PATCH 270/700] platform/x86: asus-armoury: add support for FA607NU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model FA607NU. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260329124659.3967495-3-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index a53204c24d4370..ed02574139fa95 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -592,6 +592,37 @@ static const struct dmi_system_id power_limits[] = { }, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FA607NU"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 35, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_def = 45, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_def = 54, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 25, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "FA607P"), From 5d486669b3db11ccc2d1b9f4e42c11c2766f35ba Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Sun, 29 Mar 2026 14:46:59 +0200 Subject: [PATCH 271/700] platform/x86: asus-armoury: add support for GU605MU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GU605MU. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260329124659.3967495-4-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index ed02574139fa95..c30d2b451e0131 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1340,6 +1340,34 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GU605MU"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 20, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 38, + .ppt_pl2_sppt_max = 53, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GU605M"), From bbe5ab8191a33572c11be8628c55b79246307125 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 31 Mar 2026 11:11:07 -0400 Subject: [PATCH 272/700] cgroup/cpuset: Simplify setsched decision check in task iteration loop of cpuset_can_attach() Centralize the check required to run security_task_setscheduler() in the task iteration loop of cpuset_can_attach() outside of the loop as it has no dependency on the characteristics of the tasks themselves. There is no functional change. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index d21868455341aa..58c5b7b72ccae6 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2988,7 +2988,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) struct cgroup_subsys_state *css; struct cpuset *cs, *oldcs; struct task_struct *task; - bool cpus_updated, mems_updated; + bool setsched_check; int ret; /* used later by cpuset_attach() */ @@ -3003,20 +3003,21 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) if (ret) goto out_unlock; - cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus); - mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems); + /* + * Skip rights over task setsched check in v2 when nothing changes, + * migration permission derives from hierarchy ownership in + * cgroup_procs_write_permission()). + */ + setsched_check = !cpuset_v2() || + !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || + !nodes_equal(cs->effective_mems, oldcs->effective_mems); cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) goto out_unlock; - /* - * Skip rights over task check in v2 when nothing changes, - * migration permission derives from hierarchy ownership in - * cgroup_procs_write_permission()). - */ - if (!cpuset_v2() || (cpus_updated || mems_updated)) { + if (setsched_check) { ret = security_task_setscheduler(task); if (ret) goto out_unlock; From 089f3fcd690c71cb3d8ca09f34027764e28920a0 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Tue, 31 Mar 2026 11:11:08 -0400 Subject: [PATCH 273/700] cgroup/cpuset: Skip security check for hotplug induced v1 task migration When a CPU hot removal causes a v1 cpuset to lose all its CPUs, the cpuset hotplug handler will schedule a work function to migrate tasks in that cpuset with no CPU to its ancestor to enable those tasks to continue running. If a strict security policy is in place, however, the task migration may fail when security_task_setscheduler() call in cpuset_can_attach() returns a -EACCES error. That will mean that those tasks will have no CPU to run on. The system administrators will have to explicitly intervene to either add CPUs to that cpuset or move the tasks elsewhere if they are aware of it. This problem was found by a reported test failure in the LTP's cpuset_hotplug_test.sh. Fix this problem by treating this special case as an exception to skip the setsched security check in cpuset_can_attach() when a v1 cpuset with tasks have no CPU left. With that patch applied, the cpuset_hotplug_test.sh test can be run successfully without failure. Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 58c5b7b72ccae6..1335e437098e80 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -3012,6 +3012,16 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || !nodes_equal(cs->effective_mems, oldcs->effective_mems); + /* + * A v1 cpuset with tasks will have no CPU left only when CPU hotplug + * brings the last online CPU offline as users are not allowed to empty + * cpuset.cpus when there are active tasks inside. When that happens, + * we should allow tasks to migrate out without security check to make + * sure they will be able to run after migration. + */ + if (!is_in_v2_mode() && cpumask_empty(oldcs->effective_cpus)) + setsched_check = false; + cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) From a8502a79e832b861e99218cbd2d8f4312d62e225 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 31 Mar 2026 13:42:28 -0700 Subject: [PATCH 274/700] bpf: Fix regsafe() for pointers to packet In case rold->reg->range == BEYOND_PKT_END && rcur->reg->range == N regsafe() may return true which may lead to current state with valid packet range not being explored. Fix the bug. Fixes: 6d94e741a8ff ("bpf: Support for pointers beyond pkt_end.") Signed-off-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Reviewed-by: Daniel Borkmann Reviewed-by: Amery Hung Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260331204228.26726-1-alexei.starovoitov@gmail.com --- kernel/bpf/verifier.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f108c01ff6d02c..a3388cb8fcbdf9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19915,8 +19915,13 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, * since someone could have accessed through (ptr - k), or * even done ptr -= k in a register, to get a safe access. */ - if (rold->range > rcur->range) + if (rold->range < 0 || rcur->range < 0) { + /* special case for [BEYOND|AT]_PKT_END */ + if (rold->range != rcur->range) + return false; + } else if (rold->range > rcur->range) { return false; + } /* If the offsets don't match, we can't trust our alignment; * nor can we be sure that we won't fall out of range. */ From 9ca562bb8e66978b53028fa32b1a190708e6a091 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Mon, 30 Mar 2026 16:46:24 +0800 Subject: [PATCH 275/700] net: ipv6: flowlabel: defer exclusive option free until RCU teardown `ip6fl_seq_show()` walks the global flowlabel hash under the seq-file RCU read-side lock and prints `fl->opt->opt_nflen` when an option block is present. Exclusive flowlabels currently free `fl->opt` as soon as `fl->users` drops to zero in `fl_release()`. However, the surrounding `struct ip6_flowlabel` remains visible in the global hash table until later garbage collection removes it and `fl_free_rcu()` finally tears it down. A concurrent `/proc/net/ip6_flowlabel` reader can therefore race that early `kfree()` and dereference freed option state, triggering a crash in `ip6fl_seq_show()`. Fix this by keeping `fl->opt` alive until `fl_free_rcu()`. That matches the lifetime already required for the enclosing flowlabel while readers can still reach it under RCU. Fixes: d3aedd5ebd4b ("ipv6 flowlabel: Convert hash list to RCU.") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/07351f0ec47bcee289576f39f9354f4a64add6e4.1774855883.git.zcliangcn@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_flowlabel.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 7c12bf75beedf5..c92f98c6f6ecca 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -133,11 +133,6 @@ static void fl_release(struct ip6_flowlabel *fl) if (time_after(ttd, fl->expires)) fl->expires = ttd; ttd = fl->expires; - if (fl->opt && fl->share == IPV6_FL_S_EXCL) { - struct ipv6_txoptions *opt = fl->opt; - fl->opt = NULL; - kfree(opt); - } if (!timer_pending(&ip6_fl_gc_timer) || time_after(ip6_fl_gc_timer.expires, ttd)) mod_timer(&ip6_fl_gc_timer, ttd); From c76fef7dcd9372e3476d4df5e0a72ed5919a814b Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Tue, 31 Mar 2026 23:10:20 +0200 Subject: [PATCH 276/700] bpf: Fix grace period wait for tracepoint bpf_link Recently, tracepoints were switched from using disabled preemption (which acts as RCU read section) to SRCU-fast when they are not faultable. This means that to do a proper grace period wait for programs running in such tracepoints, we must use SRCU's grace period wait. This is only for non-faultable tracepoints, faultable ones continue using RCU Tasks Trace. However, bpf_link_free() currently does call_rcu() for all cases when the link is non-sleepable (hence, for tracepoints, non-faultable). Fix this by doing a call_srcu() grace period wait. As far RCU Tasks Trace gp -> RCU gp chaining is concerned, it is deemed unnecessary for tracepoint programs. The link and program are either accessed under RCU Tasks Trace protection, or SRCU-fast protection now. The earlier logic of chaining both RCU Tasks Trace and RCU gp waits was to generalize the logic, even if it conceded an extra RCU gp wait, however that is unnecessary for tracepoints even before this change. In practice no cost was paid since rcu_trace_implies_rcu_gp() was always true. Hence we need not chaining any RCU gp after the SRCU gp. For instance, in the non-faultable raw tracepoint, the RCU read section of the program in __bpf_trace_run() is enclosed in the SRCU gp, likewise for faultable raw tracepoint, the program is under the RCU Tasks Trace protection. Hence, the outermost scope can be waited upon to ensure correctness. Also, sleepable programs cannot be attached to non-faultable tracepoints, so whenever program or link is sleepable, only RCU Tasks Trace protection is being used for the link and prog. Fixes: a46023d5616e ("tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast") Reviewed-by: Sun Jian Reviewed-by: Puranjay Mohan Acked-by: Andrii Nakryiko Signed-off-by: Kumar Kartikeya Dwivedi Acked-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20260331211021.1632902-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 4 ++++ include/linux/tracepoint.h | 20 ++++++++++++++++++++ kernel/bpf/syscall.c | 25 +++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 05b34a6355b035..35b1e25bd10437 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1854,6 +1854,10 @@ struct bpf_link_ops { * target hook is sleepable, we'll go through tasks trace RCU GP and * then "classic" RCU GP; this need for chaining tasks trace and * classic RCU GPs is designated by setting bpf_link->sleepable flag + * + * For non-sleepable tracepoint links we go through SRCU gp instead, + * since RCU is not used in that case. Sleepable tracepoints still + * follow the scheme above. */ void (*dealloc_deferred)(struct bpf_link *link); int (*detach)(struct bpf_link *link); diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 22ca1c8b54f32b..1d7f29f5e90147 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -122,6 +122,22 @@ static inline bool tracepoint_is_faultable(struct tracepoint *tp) { return tp->ext && tp->ext->faultable; } +/* + * Run RCU callback with the appropriate grace period wait for non-faultable + * tracepoints, e.g., those used in atomic context. + */ +static inline void call_tracepoint_unregister_atomic(struct rcu_head *rcu, rcu_callback_t func) +{ + call_srcu(&tracepoint_srcu, rcu, func); +} +/* + * Run RCU callback with the appropriate grace period wait for faultable + * tracepoints, e.g., those used in syscall context. + */ +static inline void call_tracepoint_unregister_syscall(struct rcu_head *rcu, rcu_callback_t func) +{ + call_rcu_tasks_trace(rcu, func); +} #else static inline void tracepoint_synchronize_unregister(void) { } @@ -129,6 +145,10 @@ static inline bool tracepoint_is_faultable(struct tracepoint *tp) { return false; } +static inline void call_tracepoint_unregister_atomic(struct rcu_head *rcu, rcu_callback_t func) +{ } +static inline void call_tracepoint_unregister_syscall(struct rcu_head *rcu, rcu_callback_t func) +{ } #endif #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 274039e36465df..700938782bed2f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3261,6 +3261,18 @@ static void bpf_link_defer_dealloc_rcu_gp(struct rcu_head *rcu) bpf_link_dealloc(link); } +static bool bpf_link_is_tracepoint(struct bpf_link *link) +{ + /* + * Only these combinations support a tracepoint bpf_link. + * BPF_LINK_TYPE_TRACING raw_tp progs are hardcoded to use + * bpf_raw_tp_link_lops and thus dealloc_deferred(), see + * bpf_raw_tp_link_attach(). + */ + return link->type == BPF_LINK_TYPE_RAW_TRACEPOINT || + (link->type == BPF_LINK_TYPE_TRACING && link->attach_type == BPF_TRACE_RAW_TP); +} + static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu) { if (rcu_trace_implies_rcu_gp()) @@ -3279,16 +3291,25 @@ static void bpf_link_free(struct bpf_link *link) if (link->prog) ops->release(link); if (ops->dealloc_deferred) { - /* Schedule BPF link deallocation, which will only then + /* + * Schedule BPF link deallocation, which will only then * trigger putting BPF program refcount. * If underlying BPF program is sleepable or BPF link's target * attach hookpoint is sleepable or otherwise requires RCU GPs * to ensure link and its underlying BPF program is not * reachable anymore, we need to first wait for RCU tasks - * trace sync, and then go through "classic" RCU grace period + * trace sync, and then go through "classic" RCU grace period. + * + * For tracepoint BPF links, we need to go through SRCU grace + * period wait instead when non-faultable tracepoint is used. We + * don't need to chain SRCU grace period waits, however, for the + * faultable case, since it exclusively uses RCU Tasks Trace. */ if (link->sleepable || (link->prog && link->prog->sleepable)) call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp); + /* We need to do a SRCU grace period wait for non-faultable tracepoint BPF links. */ + else if (bpf_link_is_tracepoint(link)) + call_tracepoint_unregister_atomic(&link->rcu, bpf_link_defer_dealloc_rcu_gp); else call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp); } else if (ops->dealloc) { From 78ec5bf2f589ec7fd8f169394bfeca541b077317 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Mon, 30 Mar 2026 13:11:27 -0700 Subject: [PATCH 277/700] fs/smb/client: fix out-of-bounds read in cifs_sanitize_prepath When cifs_sanitize_prepath is called with an empty string or a string containing only delimiters (e.g., "/"), the current logic attempts to check *(cursor2 - 1) before cursor2 has advanced. This results in an out-of-bounds read. This patch adds an early exit check after stripping prepended delimiters. If no path content remains, the function returns NULL. The bug was identified via manual audit and verified using a standalone test case compiled with AddressSanitizer, which triggered a SEGV on affected inputs. Signed-off-by: Fredric Cover Reviewed-by: Henrique Carvalho <[2]henrique.carvalho@suse.com> Signed-off-by: Steve French --- fs/smb/client/fs_context.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index a4a7c7eee038c1..a46764c247107d 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -588,6 +588,10 @@ char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) while (IS_DELIM(*cursor1)) cursor1++; + /* exit in case of only delimiters */ + if (!*cursor1) + return NULL; + /* copy the first letter */ *cursor2 = *cursor1; From 5dd8025a49c268ab6b94d978532af3ad341132a7 Mon Sep 17 00:00:00 2001 From: Li Xiasong Date: Mon, 30 Mar 2026 20:03:35 +0800 Subject: [PATCH 278/700] mptcp: fix soft lockup in mptcp_recvmsg() syzbot reported a soft lockup in mptcp_recvmsg() [0]. When receiving data with MSG_PEEK | MSG_WAITALL flags, the skb is not removed from the sk_receive_queue. This causes sk_wait_data() to always find available data and never perform actual waiting, leading to a soft lockup. Fix this by adding a 'last' parameter to track the last peeked skb. This allows sk_wait_data() to make informed waiting decisions and prevent infinite loops when MSG_PEEK is used. [0]: watchdog: BUG: soft lockup - CPU#2 stuck for 156s! [server:1963] Modules linked in: CPU: 2 UID: 0 PID: 1963 Comm: server Not tainted 6.19.0-rc8 #61 PREEMPT(none) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:sk_wait_data+0x15/0x190 Code: 80 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 41 56 41 55 41 54 49 89 f4 55 48 89 d5 53 48 89 fb <48> 83 ec 30 65 48 8b 05 17 a4 6b 01 48 89 44 24 28 31 c0 65 48 8b RSP: 0018:ffffc90000603ca0 EFLAGS: 00000246 RAX: 0000000000000000 RBX: ffff888102bf0800 RCX: 0000000000000001 RDX: 0000000000000000 RSI: ffffc90000603d18 RDI: ffff888102bf0800 RBP: 0000000000000000 R08: 0000000000000002 R09: 0000000000000101 R10: 0000000000000000 R11: 0000000000000075 R12: ffffc90000603d18 R13: ffff888102bf0800 R14: ffff888102bf0800 R15: 0000000000000000 FS: 00007f6e38b8c4c0(0000) GS:ffff8881b877e000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000055aa7bff1680 CR3: 0000000105cbe000 CR4: 00000000000006f0 Call Trace: mptcp_recvmsg+0x547/0x8c0 net/mptcp/protocol.c:2329 inet_recvmsg+0x11f/0x130 net/ipv4/af_inet.c:891 sock_recvmsg+0x94/0xc0 net/socket.c:1100 __sys_recvfrom+0xb2/0x130 net/socket.c:2256 __x64_sys_recvfrom+0x1f/0x30 net/socket.c:2267 do_syscall_64+0x59/0x2d0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:131 RIP: 0033:0x7f6e386a4a1d Code: 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 8d 05 f1 de 2c 00 41 89 ca 8b 00 85 c0 75 20 45 31 c9 45 31 c0 b8 2d 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 6b f3 c3 66 0f 1f 84 00 00 00 00 00 41 56 41 RSP: 002b:00007ffc3c4bb078 EFLAGS: 00000246 ORIG_RAX: 000000000000002d RAX: ffffffffffffffda RBX: 000000000000861e RCX: 00007f6e386a4a1d RDX: 00000000000003ff RSI: 00007ffc3c4bb150 RDI: 0000000000000004 RBP: 00007ffc3c4bb570 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000103 R11: 0000000000000246 R12: 00005605dbc00be0 R13: 00007ffc3c4bb650 R14: 0000000000000000 R15: 0000000000000000 Fixes: 8e04ce45a8db ("mptcp: fix MSG_PEEK stream corruption") Signed-off-by: Li Xiasong Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260330120335.659027-1-lixiasong1@huawei.com Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index cf1852b999637e..65c3bb8016f47e 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2006,7 +2006,7 @@ static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb) static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, size_t len, int flags, int copied_total, struct scm_timestamping_internal *tss, - int *cmsg_flags) + int *cmsg_flags, struct sk_buff **last) { struct mptcp_sock *msk = mptcp_sk(sk); struct sk_buff *skb, *tmp; @@ -2023,6 +2023,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, /* skip already peeked skbs */ if (total_data_len + data_len <= copied_total) { total_data_len += data_len; + *last = skb; continue; } @@ -2058,6 +2059,8 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg, } mptcp_eat_recv_skb(sk, skb); + } else { + *last = skb; } if (copied >= len) @@ -2288,10 +2291,12 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, cmsg_flags = MPTCP_CMSG_INQ; while (copied < len) { + struct sk_buff *last = NULL; int err, bytes_read; bytes_read = __mptcp_recvmsg_mskq(sk, msg, len - copied, flags, - copied, &tss, &cmsg_flags); + copied, &tss, &cmsg_flags, + &last); if (unlikely(bytes_read < 0)) { if (!copied) copied = bytes_read; @@ -2343,7 +2348,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, pr_debug("block timeout %ld\n", timeo); mptcp_cleanup_rbuf(msk, copied); - err = sk_wait_data(sk, &timeo, NULL); + err = sk_wait_data(sk, &timeo, last); if (err < 0) { err = copied ? : err; goto out_err; From c0fd0fe745f5e8c568d898cd1513d0083e46204a Mon Sep 17 00:00:00 2001 From: Yufan Chen Date: Sun, 29 Mar 2026 00:32:57 +0800 Subject: [PATCH 279/700] net: ftgmac100: fix ring allocation unwind on open failure ftgmac100_alloc_rings() allocates rx_skbs, tx_skbs, rxdes, txdes, and rx_scratch in stages. On intermediate failures it returned -ENOMEM directly, leaking resources allocated earlier in the function. Rework the failure path to use staged local unwind labels and free allocated resources in reverse order before returning -ENOMEM. This matches common netdev allocation cleanup style. Fixes: d72e01a0430f ("ftgmac100: Use a scratch buffer for failed RX allocations") Cc: stable@vger.kernel.org Signed-off-by: Yufan Chen Link: https://patch.msgid.link/20260328163257.60836-1-yufan.chen@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/faraday/ftgmac100.c | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 1e91e79c81349d..6d2fe5c2f3903e 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -977,19 +977,19 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *), GFP_KERNEL); if (!priv->tx_skbs) - return -ENOMEM; + goto err_free_rx_skbs; /* Allocate descriptors */ priv->rxdes = dma_alloc_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes), &priv->rxdes_dma, GFP_KERNEL); if (!priv->rxdes) - return -ENOMEM; + goto err_free_tx_skbs; priv->txdes = dma_alloc_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes), &priv->txdes_dma, GFP_KERNEL); if (!priv->txdes) - return -ENOMEM; + goto err_free_rxdes; /* Allocate scratch packet buffer */ priv->rx_scratch = dma_alloc_coherent(priv->dev, @@ -997,9 +997,29 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) &priv->rx_scratch_dma, GFP_KERNEL); if (!priv->rx_scratch) - return -ENOMEM; + goto err_free_txdes; return 0; + +err_free_txdes: + dma_free_coherent(priv->dev, + MAX_TX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_txdes), + priv->txdes, priv->txdes_dma); + priv->txdes = NULL; +err_free_rxdes: + dma_free_coherent(priv->dev, + MAX_RX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_rxdes), + priv->rxdes, priv->rxdes_dma); + priv->rxdes = NULL; +err_free_tx_skbs: + kfree(priv->tx_skbs); + priv->tx_skbs = NULL; +err_free_rx_skbs: + kfree(priv->rx_skbs); + priv->rx_skbs = NULL; + return -ENOMEM; } static void ftgmac100_init_rings(struct ftgmac100 *priv) From 48b3cd69265f346f64b93064723492da46206e9b Mon Sep 17 00:00:00 2001 From: Michal Piekos Date: Sat, 28 Mar 2026 09:55:51 +0100 Subject: [PATCH 280/700] net: stmmac: skip VLAN restore when VLAN hash ops are missing stmmac_vlan_restore() unconditionally calls stmmac_vlan_update() when NETIF_F_VLAN_FEATURES is set. On platforms where priv->hw->vlan (or ->update_vlan_hash) is not provided, stmmac_update_vlan_hash() returns -EINVAL via stmmac_do_void_callback(), resulting in a spurious "Failed to restore VLANs" error even when no VLAN filtering is in use. Remove not needed comment. Remove not used return value from stmmac_vlan_restore(). Tested on Orange Pi Zero 3. Fixes: bd7ad51253a7 ("net: stmmac: Fix VLAN HW state restore") Signed-off-by: Michal Piekos Link: https://patch.msgid.link/20260328-vlan-restore-error-v4-1-f88624c530dc@mmpsystems.pl Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 6827c99bde8c22..13d3cac056bea5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -156,7 +156,7 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue); static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue); static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, u32 rxmode, u32 chan); -static int stmmac_vlan_restore(struct stmmac_priv *priv); +static void stmmac_vlan_restore(struct stmmac_priv *priv); #ifdef CONFIG_DEBUG_FS static const struct net_device_ops stmmac_netdev_ops; @@ -6859,21 +6859,15 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi return ret; } -static int stmmac_vlan_restore(struct stmmac_priv *priv) +static void stmmac_vlan_restore(struct stmmac_priv *priv) { - int ret; - if (!(priv->dev->features & NETIF_F_VLAN_FEATURES)) - return 0; + return; if (priv->hw->num_vlan) stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw); - ret = stmmac_vlan_update(priv, priv->num_double_vlans); - if (ret) - netdev_err(priv->dev, "Failed to restore VLANs\n"); - - return ret; + stmmac_vlan_update(priv, priv->num_double_vlans); } static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf) From 8c0ef7b56d6bbbc53f2d43d99c195144f01b0775 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 11 Mar 2026 22:14:00 -0700 Subject: [PATCH 281/700] lis3lv02d: fix kernel-doc warnings Use the correct kernel-doc format to avoid kernel-doc warnings: Warning: include/linux/lis3lv02d.h:125 struct member 'st_min_limits' not described in 'lis3lv02d_platform_data' Warning: include/linux/lis3lv02d.h:125 struct member 'st_max_limits' not described in 'lis3lv02d_platform_data' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260312051400.682991-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/lis3lv02d.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h index b72b8cdba76501..feb60ba4e30e03 100644 --- a/include/linux/lis3lv02d.h +++ b/include/linux/lis3lv02d.h @@ -30,8 +30,8 @@ * @default_rate: Default sampling rate. 0 means reset default * @setup_resources: Interrupt line setup call back function * @release_resources: Interrupt line release call back function - * @st_min_limits[3]: Selftest acceptance minimum values - * @st_max_limits[3]: Selftest acceptance maximum values + * @st_min_limits: Selftest acceptance minimum values (x, y, z) + * @st_max_limits: Selftest acceptance maximum values (x, y, z) * @irq2: Irq line 2 number * * Platform data is used to setup the sensor chip. Meaning of the different From f40b1401b6ad0f4dadfca4e7a69744352a2e4f8f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Mar 2026 19:04:36 +0100 Subject: [PATCH 282/700] lis3lv02d: Omit IRQF_ONESHOT if no threaded handler is provided The lis3lv02d started triggering a WARN in the IRQ code because it passes IRQF_ONESHOT to request_threaded_irq() even when thread_fn is NULL, which is an invalid combination. So set the flag only if thread_fn is non-NULL. Cc: Eric Piel Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Signed-off-by: Ard Biesheuvel Link: https://patch.msgid.link/20260326180436.14968-2-ardb@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 9c68f8b1d5d653..21e8ad0a744429 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -1230,10 +1230,12 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3) else thread_fn = NULL; + if (thread_fn) + irq_flags |= IRQF_ONESHOT; + err = request_threaded_irq(lis3->irq, lis302dl_interrupt, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT | - irq_flags, + irq_flags | IRQF_TRIGGER_RISING, DRIVER_NAME, lis3); if (err < 0) { From e8d0ed37bd51da52da6225d278e330c2f18a6198 Mon Sep 17 00:00:00 2001 From: Ernestas Kulik Date: Tue, 24 Mar 2026 13:07:16 +0200 Subject: [PATCH 283/700] USB: serial: option: add MeiG Smart SRM825WN Add support for the SDX62-based MeiG Smart SRM825WN module. If#= 0: RNDIS If#= 1: RNDIS If#= 2: Diag If#= 3: AT If#= 4: AT If#= 5: NMEA T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 19 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2dee ProdID=4d38 Rev= 5.04 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=da47a175 C:* #Ifs= 6 Cfg#= 1 Atr=80 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03 I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Ernestas Kulik Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d222bf2c253913..313612114db905 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2441,6 +2441,9 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM815 and SRM825L */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825L */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825L */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ From 76522fcdbc3a02b568f5d957f7e66fc194abb893 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 26 Mar 2026 00:17:09 +0100 Subject: [PATCH 284/700] netfilter: flowtable: strictly check for maximum number of actions The maximum number of flowtable hardware offload actions in IPv6 is: * ethernet mangling (4 payload actions, 2 for each ethernet address) * SNAT (4 payload actions) * DNAT (4 payload actions) * Double VLAN (4 vlan actions, 2 for popping vlan, and 2 for pushing) for QinQ. * Redirect (1 action) Which makes 17, while the maximum is 16. But act_ct supports for tunnels actions too. Note that payload action operates at 32-bit word level, so mangling an IPv6 address takes 4 payload actions. Update flow_action_entry_next() calls to check for the maximum number of supported actions. While at it, rise the maximum number of actions per flow from 16 to 24 so this works fine with IPv6 setups. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Reported-by: Hyunwoo Kim Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 196 +++++++++++++++++--------- 1 file changed, 130 insertions(+), 66 deletions(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 9b677e1164873d..93d0aa7f8fcc1f 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -14,6 +14,8 @@ #include #include +#define NF_FLOW_RULE_ACTION_MAX 24 + static struct workqueue_struct *nf_flow_offload_add_wq; static struct workqueue_struct *nf_flow_offload_del_wq; static struct workqueue_struct *nf_flow_offload_stats_wq; @@ -216,7 +218,12 @@ static void flow_offload_mangle(struct flow_action_entry *entry, static inline struct flow_action_entry * flow_action_entry_next(struct nf_flow_rule *flow_rule) { - int i = flow_rule->rule->action.num_entries++; + int i; + + if (unlikely(flow_rule->rule->action.num_entries >= NF_FLOW_RULE_ACTION_MAX)) + return NULL; + + i = flow_rule->rule->action.num_entries++; return &flow_rule->rule->action.entries[i]; } @@ -234,6 +241,9 @@ static int flow_offload_eth_src(struct net *net, u32 mask, val; u16 val16; + if (!entry0 || !entry1) + return -E2BIG; + this_tuple = &flow->tuplehash[dir].tuple; switch (this_tuple->xmit_type) { @@ -284,6 +294,9 @@ static int flow_offload_eth_dst(struct net *net, u8 nud_state; u16 val16; + if (!entry0 || !entry1) + return -E2BIG; + this_tuple = &flow->tuplehash[dir].tuple; switch (this_tuple->xmit_type) { @@ -325,16 +338,19 @@ static int flow_offload_eth_dst(struct net *net, return 0; } -static void flow_offload_ipv4_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask = ~htonl(0xffffffff); __be32 addr; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4.s_addr; @@ -345,23 +361,27 @@ static void flow_offload_ipv4_snat(struct net *net, offset = offsetof(struct iphdr, daddr); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, &addr, &mask); + return 0; } -static void flow_offload_ipv4_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask = ~htonl(0xffffffff); __be32 addr; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4.s_addr; @@ -372,14 +392,15 @@ static void flow_offload_ipv4_dnat(struct net *net, offset = offsetof(struct iphdr, saddr); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, &addr, &mask); + return 0; } -static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, +static int flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, unsigned int offset, const __be32 *addr, const __be32 *mask) { @@ -388,15 +409,20 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; + flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6, offset + i * sizeof(u32), &addr[i], mask); } + + return 0; } -static void flow_offload_ipv6_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv6_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); const __be32 *addr; @@ -412,16 +438,16 @@ static void flow_offload_ipv6_snat(struct net *net, offset = offsetof(struct ipv6hdr, daddr); break; default: - return; + return -EOPNOTSUPP; } - flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); + return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } -static void flow_offload_ipv6_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv6_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); const __be32 *addr; @@ -437,10 +463,10 @@ static void flow_offload_ipv6_dnat(struct net *net, offset = offsetof(struct ipv6hdr, saddr); break; default: - return; + return -EOPNOTSUPP; } - flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); + return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } static int flow_offload_l4proto(const struct flow_offload *flow) @@ -462,15 +488,18 @@ static int flow_offload_l4proto(const struct flow_offload *flow) return type; } -static void flow_offload_port_snat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_port_snat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask, port; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port); @@ -485,22 +514,26 @@ static void flow_offload_port_snat(struct net *net, mask = ~htonl(0xffff); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, &port, &mask); + return 0; } -static void flow_offload_port_dnat(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_port_dnat(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); u32 mask, port; u32 offset; + if (!entry) + return -E2BIG; + switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port); @@ -515,20 +548,24 @@ static void flow_offload_port_dnat(struct net *net, mask = ~htonl(0xffff0000); break; default: - return; + return -EOPNOTSUPP; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, &port, &mask); + return 0; } -static void flow_offload_ipv4_checksum(struct net *net, - const struct flow_offload *flow, - struct nf_flow_rule *flow_rule) +static int flow_offload_ipv4_checksum(struct net *net, + const struct flow_offload *flow, + struct nf_flow_rule *flow_rule) { u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto; struct flow_action_entry *entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; + entry->id = FLOW_ACTION_CSUM; entry->csum_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR; @@ -540,12 +577,14 @@ static void flow_offload_ipv4_checksum(struct net *net, entry->csum_flags |= TCA_CSUM_UPDATE_FLAG_UDP; break; } + + return 0; } -static void flow_offload_redirect(struct net *net, - const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_redirect(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *this_tuple, *other_tuple; struct flow_action_entry *entry; @@ -563,21 +602,28 @@ static void flow_offload_redirect(struct net *net, ifindex = other_tuple->iifidx; break; default: - return; + return -EOPNOTSUPP; } dev = dev_get_by_index(net, ifindex); if (!dev) - return; + return -ENODEV; entry = flow_action_entry_next(flow_rule); + if (!entry) { + dev_put(dev); + return -E2BIG; + } + entry->id = FLOW_ACTION_REDIRECT; entry->dev = dev; + + return 0; } -static void flow_offload_encap_tunnel(const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_encap_tunnel(const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *this_tuple; struct flow_action_entry *entry; @@ -585,7 +631,7 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow, this_tuple = &flow->tuplehash[dir].tuple; if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) - return; + return 0; dst = this_tuple->dst_cache; if (dst && dst->lwtstate) { @@ -594,15 +640,19 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow, tun_info = lwt_tun_info(dst->lwtstate); if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; entry->id = FLOW_ACTION_TUNNEL_ENCAP; entry->tunnel = tun_info; } } + + return 0; } -static void flow_offload_decap_tunnel(const struct flow_offload *flow, - enum flow_offload_tuple_dir dir, - struct nf_flow_rule *flow_rule) +static int flow_offload_decap_tunnel(const struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) { const struct flow_offload_tuple *other_tuple; struct flow_action_entry *entry; @@ -610,7 +660,7 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow, other_tuple = &flow->tuplehash[!dir].tuple; if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) - return; + return 0; dst = other_tuple->dst_cache; if (dst && dst->lwtstate) { @@ -619,9 +669,13 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow, tun_info = lwt_tun_info(dst->lwtstate); if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -E2BIG; entry->id = FLOW_ACTION_TUNNEL_DECAP; } } + + return 0; } static int @@ -633,8 +687,9 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, const struct flow_offload_tuple *tuple; int i; - flow_offload_decap_tunnel(flow, dir, flow_rule); - flow_offload_encap_tunnel(flow, dir, flow_rule); + if (flow_offload_decap_tunnel(flow, dir, flow_rule) < 0 || + flow_offload_encap_tunnel(flow, dir, flow_rule) < 0) + return -1; if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 || flow_offload_eth_dst(net, flow, dir, flow_rule) < 0) @@ -650,6 +705,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, if (tuple->encap[i].proto == htons(ETH_P_8021Q)) { entry = flow_action_entry_next(flow_rule); + if (!entry) + return -1; entry->id = FLOW_ACTION_VLAN_POP; } } @@ -663,6 +720,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, continue; entry = flow_action_entry_next(flow_rule); + if (!entry) + return -1; switch (other_tuple->encap[i].proto) { case htons(ETH_P_PPP_SES): @@ -688,18 +747,22 @@ int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow, return -1; if (test_bit(NF_FLOW_SNAT, &flow->flags)) { - flow_offload_ipv4_snat(net, flow, dir, flow_rule); - flow_offload_port_snat(net, flow, dir, flow_rule); + if (flow_offload_ipv4_snat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_snat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_DNAT, &flow->flags)) { - flow_offload_ipv4_dnat(net, flow, dir, flow_rule); - flow_offload_port_dnat(net, flow, dir, flow_rule); + if (flow_offload_ipv4_dnat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_dnat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_SNAT, &flow->flags) || test_bit(NF_FLOW_DNAT, &flow->flags)) - flow_offload_ipv4_checksum(net, flow, flow_rule); + if (flow_offload_ipv4_checksum(net, flow, flow_rule) < 0) + return -1; - flow_offload_redirect(net, flow, dir, flow_rule); + if (flow_offload_redirect(net, flow, dir, flow_rule) < 0) + return -1; return 0; } @@ -713,22 +776,23 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow, return -1; if (test_bit(NF_FLOW_SNAT, &flow->flags)) { - flow_offload_ipv6_snat(net, flow, dir, flow_rule); - flow_offload_port_snat(net, flow, dir, flow_rule); + if (flow_offload_ipv6_snat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_snat(net, flow, dir, flow_rule) < 0) + return -1; } if (test_bit(NF_FLOW_DNAT, &flow->flags)) { - flow_offload_ipv6_dnat(net, flow, dir, flow_rule); - flow_offload_port_dnat(net, flow, dir, flow_rule); + if (flow_offload_ipv6_dnat(net, flow, dir, flow_rule) < 0 || + flow_offload_port_dnat(net, flow, dir, flow_rule) < 0) + return -1; } - flow_offload_redirect(net, flow, dir, flow_rule); + if (flow_offload_redirect(net, flow, dir, flow_rule) < 0) + return -1; return 0; } EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6); -#define NF_FLOW_RULE_ACTION_MAX 16 - static struct nf_flow_rule * nf_flow_offload_rule_alloc(struct net *net, const struct flow_offload_work *offload, From 6d52a4a0520a6696bdde51caa11f2d6821cd0c01 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 26 Mar 2026 16:17:24 +0100 Subject: [PATCH 285/700] netfilter: nfnetlink_log: account for netlink header size This is a followup to an old bug fix: NLMSG_DONE needs to account for the netlink header size, not just the attribute size. This can result in a WARN splat + drop of the netlink message, but other than this there are no ill effects. Fixes: 9dfa1dfe4d5e ("netfilter: nf_log: account for size of NLMSG_DONE attribute") Reported-by: Yiming Qian Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fcbe54940b2ec3..f80978c06fa0a0 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -726,7 +726,7 @@ nfulnl_log_packet(struct net *net, + nla_total_size(plen) /* prefix */ + nla_total_size(sizeof(struct nfulnl_msg_packet_hw)) + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)) - + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */ + + nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */ if (in && skb_mac_header_was_set(skb)) { size += nla_total_size(skb->dev->hard_header_len) From a958a4f90ddd7de0800b33ca9d7b886b7d40f74e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 31 Mar 2026 23:13:36 +0200 Subject: [PATCH 286/700] netfilter: x_tables: ensure names are nul-terminated Reject names that lack a \0 character before feeding them to functions that expect c-strings. Fixes tag is the most recent commit that needs this change. Fixes: c38c4597e4bf ("netfilter: implement xt_cgroup cgroup2 path match") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_cgroup.c | 6 ++++++ net/netfilter/xt_rateest.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c index c437fbd59ec130..43d2ae2be628dc 100644 --- a/net/netfilter/xt_cgroup.c +++ b/net/netfilter/xt_cgroup.c @@ -65,6 +65,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par) info->priv = NULL; if (info->has_path) { + if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path)) + return -ENAMETOOLONG; + cgrp = cgroup_get_from_path(info->path); if (IS_ERR(cgrp)) { pr_info_ratelimited("invalid path, errno=%ld\n", @@ -102,6 +105,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par) info->priv = NULL; if (info->has_path) { + if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path)) + return -ENAMETOOLONG; + cgrp = cgroup_get_from_path(info->path); if (IS_ERR(cgrp)) { pr_info_ratelimited("invalid path, errno=%ld\n", diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 72324bd976af8c..b1d736c15fcbe5 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) goto err1; } + if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1)) + return -ENAMETOOLONG; + if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2)) + return -ENAMETOOLONG; + ret = -ENOENT; est1 = xt_rateest_lookup(par->net, info->name1); if (!est1) From b7e8590987aa94c9dc51518fad0e58cb887b1db5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 30 Mar 2026 14:16:34 +0200 Subject: [PATCH 287/700] netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr IPSET_ATTR_NAME and IPSET_ATTR_NAMEREF are of NLA_STRING type, they cannot be treated like a c-string. They either have to be switched to NLA_NUL_STRING, or the compare operations need to use the nla functions. Fixes: f830837f0eed ("netfilter: ipset: list:set set type support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 2 +- net/netfilter/ipset/ip_set_core.c | 4 ++-- net/netfilter/ipset/ip_set_list_set.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e9f4f845d760af..b98331572ad298 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -309,7 +309,7 @@ enum { /* register and unregister set references */ extern ip_set_id_t ip_set_get_byname(struct net *net, - const char *name, struct ip_set **set); + const struct nlattr *name, struct ip_set **set); extern void ip_set_put_byindex(struct net *net, ip_set_id_t index); extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name); extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a2fe711cb5e367..d0c9fe59c67d2a 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del); * */ ip_set_id_t -ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) +ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; @@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) rcu_read_lock(); for (i = 0; i < inst->ip_set_max; i++) { s = rcu_dereference(inst->ip_set_list)[i]; - if (s && STRNCMP(s->name, name)) { + if (s && nla_strcmp(name, s->name) == 0) { __ip_set_get(s); index = i; *set = s; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 98b91b34c3146c..1cef84f15e8c75 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], ret = ip_set_get_extensions(set, tb, &ext); if (ret) return ret; - e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s); + e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s); if (e.id == IPSET_INVALID_ID) return -IPSET_ERR_NAME; /* "Loop detection" */ @@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_NAMEREF]) { e.refid = ip_set_get_byname(map->net, - nla_data(tb[IPSET_ATTR_NAMEREF]), + tb[IPSET_ATTR_NAMEREF], &s); if (e.refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; From a242a9ae58aa46ff7dae51ce64150a93957abe65 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Mon, 30 Mar 2026 00:50:36 +0800 Subject: [PATCH 288/700] netfilter: nf_conntrack_helper: pass helper to expect cleanup nf_conntrack_helper_unregister() calls nf_ct_expect_iterate_destroy() to remove expectations belonging to the helper being unregistered. However, it passes NULL instead of the helper pointer as the data argument, so expect_iter_me() never matches any expectation and all of them survive the cleanup. After unregister returns, nfnl_cthelper_del() frees the helper object immediately. Subsequent expectation dumps or packet-driven init_conntrack() calls then dereference the freed exp->helper, causing a use-after-free. Pass the actual helper pointer so expectations referencing it are properly destroyed before the helper object is freed. BUG: KASAN: slab-use-after-free in string+0x38f/0x430 Read of size 1 at addr ffff888003b14d20 by task poc/103 Call Trace: string+0x38f/0x430 vsnprintf+0x3cc/0x1170 seq_printf+0x17a/0x240 exp_seq_show+0x2e5/0x560 seq_read_iter+0x419/0x1280 proc_reg_read+0x1ac/0x270 vfs_read+0x179/0x930 ksys_read+0xef/0x1c0 Freed by task 103: The buggy address is located 32 bytes inside of freed 192-byte region [ffff888003b14d00, ffff888003b14dc0) Fixes: ac7b84839003 ("netfilter: expect: add and use nf_ct_expect_iterate helpers") Signed-off-by: Qi Tang Reviewed-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 1b330ba6613bb2..a715304a53d8c2 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -415,7 +415,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) */ synchronize_rcu(); - nf_ct_expect_iterate_destroy(expect_iter_me, NULL); + nf_ct_expect_iterate_destroy(expect_iter_me, me); nf_ct_iterate_destroy(unhelp, me); /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as From 35177c6877134a21315f37d57a5577846225623e Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Tue, 31 Mar 2026 14:17:12 +0800 Subject: [PATCH 289/700] netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent ctnetlink_alloc_expect() allocates expectations from a non-zeroing slab cache via nf_ct_expect_alloc(). When CTA_EXPECT_NAT is not present in the netlink message, saved_addr and saved_proto are never initialized. Stale data from a previous slab occupant can then be dumped to userspace by ctnetlink_exp_dump_expect(), which checks these fields to decide whether to emit CTA_EXPECT_NAT. The safe sibling nf_ct_expect_init(), used by the packet path, explicitly zeroes these fields. Zero saved_addr, saved_proto and dir in the else branch, guarded by IS_ENABLED(CONFIG_NF_NAT) since these fields only exist when NAT is enabled. Confirmed by priming the expect slab with NAT-bearing expectations, freeing them, creating a new expectation without CTA_EXPECT_NAT, and observing that the ctnetlink dump emits a spurious CTA_EXPECT_NAT containing stale data from the prior allocation. Fixes: 076a0ca02644 ("netfilter: ctnetlink: add NAT support for expectations") Reported-by: kernel test robot Signed-off-by: Qi Tang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 3f408f3713bb33..38bd7124d9f742 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3588,6 +3588,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp, nf_ct_l3num(ct)); if (err < 0) goto err_out; +#if IS_ENABLED(CONFIG_NF_NAT) + } else { + memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); + memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); + exp->dir = 0; +#endif } return exp; err_out: From 917b61fa2042f11e2af4c428e43f08199586633a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Mar 2026 11:26:22 +0200 Subject: [PATCH 290/700] netfilter: ctnetlink: ignore explicit helper on new expectations Use the existing master conntrack helper, anything else is not really supported and it just makes validation more complicated, so just ignore what helper userspace suggests for this expectation. This was uncovered when validating CTA_EXPECT_CLASS via different helper provided by userspace than the existing master conntrack helper: BUG: KASAN: slab-out-of-bounds in nf_ct_expect_related_report+0x2479/0x27c0 Read of size 4 at addr ffff8880043fe408 by task poc/102 Call Trace: nf_ct_expect_related_report+0x2479/0x27c0 ctnetlink_create_expect+0x22b/0x3b0 ctnetlink_new_expect+0x4bd/0x5c0 nfnetlink_rcv_msg+0x67a/0x950 netlink_rcv_skb+0x120/0x350 Allowing to read kernel memory bytes off the expectation boundary. CTA_EXPECT_HELP_NAME is still used to offer the helper name to userspace via netlink dump. Fixes: bd0779370588 ("netfilter: nfnetlink_queue: allow to attach expectations to conntracks") Reported-by: Qi Tang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 54 +++++----------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 38bd7124d9f742..a20cd82446c543 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2636,7 +2636,6 @@ 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, - struct nf_conntrack_helper *helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask); @@ -2865,7 +2864,6 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, { struct nlattr *cda[CTA_EXPECT_MAX+1]; struct nf_conntrack_tuple tuple, mask; - struct nf_conntrack_helper *helper = NULL; struct nf_conntrack_expect *exp; int err; @@ -2879,17 +2877,8 @@ 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]); - - helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), - nf_ct_protonum(ct)); - if (helper == NULL) - return -EOPNOTSUPP; - } - exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, - helper, &tuple, &mask); + &tuple, &mask); if (IS_ERR(exp)) return PTR_ERR(exp); @@ -3528,11 +3517,11 @@ 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, - struct nf_conntrack_helper *helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask) { struct net *net = read_pnet(&ct->ct_net); + struct nf_conntrack_helper *helper; struct nf_conntrack_expect *exp; struct nf_conn_help *help; u32 class = 0; @@ -3542,7 +3531,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, if (!help) return ERR_PTR(-EOPNOTSUPP); - if (cda[CTA_EXPECT_CLASS] && helper) { + helper = rcu_dereference(help->helper); + if (!helper) + return ERR_PTR(-EOPNOTSUPP); + + if (cda[CTA_EXPECT_CLASS]) { class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS])); if (class > helper->expect_class_max) return ERR_PTR(-EINVAL); @@ -3576,8 +3569,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; #endif - if (!helper) - helper = rcu_dereference(help->helper); rcu_assign_pointer(exp->helper, helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; @@ -3609,7 +3600,6 @@ ctnetlink_create_expect(struct net *net, { struct nf_conntrack_tuple tuple, mask, master_tuple; struct nf_conntrack_tuple_hash *h = NULL; - struct nf_conntrack_helper *helper = NULL; struct nf_conntrack_expect *exp; struct nf_conn *ct; int err; @@ -3635,33 +3625,7 @@ ctnetlink_create_expect(struct net *net, ct = nf_ct_tuplehash_to_ctrack(h); rcu_read_lock(); - if (cda[CTA_EXPECT_HELP_NAME]) { - const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); - - helper = __nf_conntrack_helper_find(helpname, u3, - nf_ct_protonum(ct)); - if (helper == NULL) { - rcu_read_unlock(); -#ifdef CONFIG_MODULES - if (request_module("nfct-helper-%s", helpname) < 0) { - err = -EOPNOTSUPP; - goto err_ct; - } - rcu_read_lock(); - helper = __nf_conntrack_helper_find(helpname, u3, - nf_ct_protonum(ct)); - if (helper) { - err = -EAGAIN; - goto err_rcu; - } - rcu_read_unlock(); -#endif - err = -EOPNOTSUPP; - goto err_ct; - } - } - - exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask); + exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto err_rcu; @@ -3671,8 +3635,8 @@ ctnetlink_create_expect(struct net *net, nf_ct_expect_put(exp); err_rcu: rcu_read_unlock(); -err_ct: nf_ct_put(ct); + return err; } From 9862ef9ab0a116c6dca98842aab7de13a252ae02 Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Mon, 30 Mar 2026 14:39:24 -0700 Subject: [PATCH 291/700] netfilter: ipset: drop logically empty buckets in mtype_del mtype_del() counts empty slots below n->pos in k, but it only drops the bucket when both n->pos and k are zero. This misses buckets whose live entries have all been removed while n->pos still points past deleted slots. Treat a bucket as empty when all positions below n->pos are unused and release it directly instead of shrinking it further. Fixes: 8af1c6fbd923 ("netfilter: ipset: Fix forceadd evaluation path") Cc: stable@vger.kernel.org Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Yifan Wu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Reviewed-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 181daa9c2019bd..b79e5dd2af0367 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1098,7 +1098,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!test_bit(i, n->used)) k++; } - if (n->pos == 0 && k == 0) { + if (k == n->pos) { t->hregion[r].ext_size -= ext_size(n->size, dsize); rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); From 3d5d488f11776738deab9da336038add95d342d1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 31 Mar 2026 16:41:25 +0200 Subject: [PATCH 292/700] netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP Weiming Shi says: xt_match and xt_target structs registered with NFPROTO_UNSPEC can be loaded by any protocol family through nft_compat. When such a match/target sets .hooks to restrict which hooks it may run on, the bitmask uses NF_INET_* constants. This is only correct for families whose hook layout matches NF_INET_*: IPv4, IPv6, INET, and bridge all share the same five hooks (PRE_ROUTING ... POST_ROUTING). ARP only has three hooks (IN=0, OUT=1, FORWARD=2) with different semantics. Because NF_ARP_OUT == 1 == NF_INET_LOCAL_IN, the .hooks validation silently passes for the wrong reasons, allowing matches to run on ARP chains where the hook assumptions (e.g. state->in being set on input hooks) do not hold. This leads to NULL pointer dereferences; xt_devgroup is one concrete example: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000044: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000220-0x0000000000000227] RIP: 0010:devgroup_mt+0xff/0x350 Call Trace: nft_match_eval (net/netfilter/nft_compat.c:407) nft_do_chain (net/netfilter/nf_tables_core.c:285) nft_do_chain_arp (net/netfilter/nft_chain_filter.c:61) nf_hook_slow (net/netfilter/core.c:623) arp_xmit (net/ipv4/arp.c:666) Kernel panic - not syncing: Fatal exception in interrupt Fix it by restricting arptables to NFPROTO_ARP extensions only. Note that arptables-legacy only supports: - arpt_CLASSIFY - arpt_mangle - arpt_MARK that provide explicit NFPROTO_ARP match/target declarations. Fixes: 9291747f118d ("netfilter: xtables: add device group match") Reported-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/x_tables.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index e594b3b7ad827b..b39017c8054842 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -501,6 +501,17 @@ int xt_check_match(struct xt_mtchk_param *par, par->match->table, par->table); return -EINVAL; } + + /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with + * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP + * support. + */ + if (par->family == NFPROTO_ARP && + par->match->family != NFPROTO_ARP) { + pr_info_ratelimited("%s_tables: %s match: not valid for this family\n", + xt_prefix[par->family], par->match->name); + return -EINVAL; + } if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { char used[64], allow[64]; @@ -1016,6 +1027,18 @@ int xt_check_target(struct xt_tgchk_param *par, par->target->table, par->table); return -EINVAL; } + + /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with + * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP + * support. + */ + if (par->family == NFPROTO_ARP && + par->target->family != NFPROTO_ARP) { + pr_info_ratelimited("%s_tables: %s target: not valid for this family\n", + xt_prefix[par->family], par->target->name); + return -EINVAL; + } + if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) { char used[64], allow[64]; From da107398cbd4bbdb6bffecb2ce86d5c9384f4cec Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 31 Mar 2026 23:08:02 +0200 Subject: [PATCH 293/700] netfilter: nf_tables: reject immediate NF_QUEUE verdict nft_queue is always used from userspace nftables to deliver the NF_QUEUE verdict. Immediately emitting an NF_QUEUE verdict is never used by the userspace nft tools, so reject immediate NF_QUEUE verdicts. The arp family does not provide queue support, but such an immediate verdict is still reachable. Globally reject NF_QUEUE immediate verdicts to address this issue. Fixes: f342de4e2f33 ("netfilter: nf_tables: reject QUEUE/DROP verdict parameters") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3922cff1bb3d96..8c42247a176c7b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -11667,8 +11667,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, switch (data->verdict.code) { case NF_ACCEPT: case NF_DROP: - case NF_QUEUE: - break; case NFT_CONTINUE: case NFT_BREAK: case NFT_RETURN: @@ -11703,6 +11701,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, data->verdict.chain = chain; break; + case NF_QUEUE: + /* The nft_queue expression is used for this purpose, an + * immediate NF_QUEUE verdict should not ever be seen here. + */ + fallthrough; default: return -EINVAL; } From 88c4bd90725557796c15878b7cb70066e9e6b5ab Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Thu, 3 Apr 2025 15:10:51 +0200 Subject: [PATCH 294/700] firmware: thead: Fix buffer overflow and use standard endian macros Addresses two issues in the TH1520 AON firmware protocol driver: 1. Fix a potential buffer overflow where the code used unsafe pointer arithmetic to access the 'mode' field through the 'resource' pointer with an offset. This was flagged by Smatch static checker as: "buffer overflow 'data' 2 <= 3" 2. Replace custom RPC_SET_BE* and RPC_GET_BE* macros with standard kernel endianness conversion macros (cpu_to_be16, etc.) for better portability and maintainability. The functionality was re-tested with the GPU power-up sequence, confirming the GPU powers up correctly and the driver probes successfully. [ 12.702370] powervr ffef400000.gpu: [drm] loaded firmware powervr/rogue_36.52.104.182_v1.fw [ 12.711043] powervr ffef400000.gpu: [drm] FW version v1.0 (build 6645434 OS) [ 12.719787] [drm] Initialized powervr 1.0.0 for ffef400000.gpu on minor 0 Fixes: e4b3cbd840e5 ("firmware: thead: Add AON firmware protocol driver") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/17a0ccce-060b-4b9d-a3c4-8d5d5823b1c9@stanley.mountain/ Signed-off-by: Michal Wilczynski Reviewed-by: Dan Carpenter Acked-by: Drew Fustini Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/firmware/thead,th1520-aon.c | 7 +- .../linux/firmware/thead/thead,th1520-aon.h | 74 ------------------- 2 files changed, 3 insertions(+), 78 deletions(-) diff --git a/drivers/firmware/thead,th1520-aon.c b/drivers/firmware/thead,th1520-aon.c index a35fd5e2a07f6c..d7f19b5b5f468c 100644 --- a/drivers/firmware/thead,th1520-aon.c +++ b/drivers/firmware/thead,th1520-aon.c @@ -170,10 +170,9 @@ int th1520_aon_power_update(struct th1520_aon_chan *aon_chan, u16 rsrc, hdr->func = TH1520_AON_PM_FUNC_SET_RESOURCE_POWER_MODE; hdr->size = TH1520_AON_RPC_MSG_NUM; - RPC_SET_BE16(&msg.resource, 0, rsrc); - RPC_SET_BE16(&msg.resource, 2, - (power_on ? TH1520_AON_PM_PW_MODE_ON : - TH1520_AON_PM_PW_MODE_OFF)); + msg.resource = cpu_to_be16(rsrc); + msg.mode = cpu_to_be16(power_on ? TH1520_AON_PM_PW_MODE_ON : + TH1520_AON_PM_PW_MODE_OFF); ret = th1520_aon_call_rpc(aon_chan, &msg); if (ret) diff --git a/include/linux/firmware/thead/thead,th1520-aon.h b/include/linux/firmware/thead/thead,th1520-aon.h index dae132b66873a8..d81f5f6f5b905a 100644 --- a/include/linux/firmware/thead/thead,th1520-aon.h +++ b/include/linux/firmware/thead/thead,th1520-aon.h @@ -97,80 +97,6 @@ struct th1520_aon_rpc_ack_common { #define RPC_GET_SVC_FLAG_ACK_TYPE(MESG) (((MESG)->svc & 0x40) >> 6) #define RPC_SET_SVC_FLAG_ACK_TYPE(MESG, ACK) ((MESG)->svc |= (ACK) << 6) -#define RPC_SET_BE64(MESG, OFFSET, SET_DATA) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - u64 _set_data = (SET_DATA); \ - data[_offset + 7] = _set_data & 0xFF; \ - data[_offset + 6] = (_set_data & 0xFF00) >> 8; \ - data[_offset + 5] = (_set_data & 0xFF0000) >> 16; \ - data[_offset + 4] = (_set_data & 0xFF000000) >> 24; \ - data[_offset + 3] = (_set_data & 0xFF00000000) >> 32; \ - data[_offset + 2] = (_set_data & 0xFF0000000000) >> 40; \ - data[_offset + 1] = (_set_data & 0xFF000000000000) >> 48; \ - data[_offset + 0] = (_set_data & 0xFF00000000000000) >> 56; \ - } while (0) - -#define RPC_SET_BE32(MESG, OFFSET, SET_DATA) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - u64 _set_data = (SET_DATA); \ - data[_offset + 3] = (_set_data) & 0xFF; \ - data[_offset + 2] = (_set_data & 0xFF00) >> 8; \ - data[_offset + 1] = (_set_data & 0xFF0000) >> 16; \ - data[_offset + 0] = (_set_data & 0xFF000000) >> 24; \ - } while (0) - -#define RPC_SET_BE16(MESG, OFFSET, SET_DATA) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - u64 _set_data = (SET_DATA); \ - data[_offset + 1] = (_set_data) & 0xFF; \ - data[_offset + 0] = (_set_data & 0xFF00) >> 8; \ - } while (0) - -#define RPC_SET_U8(MESG, OFFSET, SET_DATA) \ - do { \ - u8 *data = (u8 *)(MESG); \ - data[OFFSET] = (SET_DATA) & 0xFF; \ - } while (0) - -#define RPC_GET_BE64(MESG, OFFSET, PTR) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - *(u32 *)(PTR) = \ - (data[_offset + 7] | data[_offset + 6] << 8 | \ - data[_offset + 5] << 16 | data[_offset + 4] << 24 | \ - data[_offset + 3] << 32 | data[_offset + 2] << 40 | \ - data[_offset + 1] << 48 | data[_offset + 0] << 56); \ - } while (0) - -#define RPC_GET_BE32(MESG, OFFSET, PTR) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - *(u32 *)(PTR) = \ - (data[_offset + 3] | data[_offset + 2] << 8 | \ - data[_offset + 1] << 16 | data[_offset + 0] << 24); \ - } while (0) - -#define RPC_GET_BE16(MESG, OFFSET, PTR) \ - do { \ - u8 *data = (u8 *)(MESG); \ - u64 _offset = (OFFSET); \ - *(u16 *)(PTR) = (data[_offset + 1] | data[_offset + 0] << 8); \ - } while (0) - -#define RPC_GET_U8(MESG, OFFSET, PTR) \ - do { \ - u8 *data = (u8 *)(MESG); \ - *(u8 *)(PTR) = (data[OFFSET]); \ - } while (0) - /* * Defines for SC PM Power Mode */ From e91d5f94acf68618ea3ad9c92ac28614e791ae7d Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 20 Mar 2026 16:43:46 +0800 Subject: [PATCH 295/700] pmdomain: imx8mp-blk-ctrl: Keep the NOC_HDCP clock enabled Keep the NOC_HDCP clock always enabled to fix the potential hang caused by the NoC ADB400 port power down handshake. Fixes: 77b0ddb42add ("soc: imx: add i.MX8MP HDMI blk ctrl HDCP/HRV_MWR") Signed-off-by: Jacky Bai Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/imx/imx8mp-blk-ctrl.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c index 8fc79f9723f07e..3f5b9499d30a0c 100644 --- a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c @@ -352,9 +352,6 @@ static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); break; - case IMX8MP_HDMIBLK_PD_HDCP: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); - break; case IMX8MP_HDMIBLK_PD_HRV: regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); @@ -408,9 +405,6 @@ static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); break; - case IMX8MP_HDMIBLK_PD_HDCP: - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); - break; case IMX8MP_HDMIBLK_PD_HRV: regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); @@ -439,7 +433,7 @@ static int imx8mp_hdmi_power_notifier(struct notifier_block *nb, regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0); regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0); regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(0) | BIT(1) | BIT(10)); + BIT(0) | BIT(1) | BIT(10) | BIT(11)); regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0)); /* From 6dcf9d0064ce2f3e3dfe5755f98b93abe6a98e1e Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 1 Apr 2026 10:45:35 +0800 Subject: [PATCH 296/700] cpufreq: governor: fix double free in cpufreq_dbs_governor_init() error path When kobject_init_and_add() fails, cpufreq_dbs_governor_init() calls kobject_put(&dbs_data->attr_set.kobj). The kobject release callback cpufreq_dbs_data_release() calls gov->exit(dbs_data) and kfree(dbs_data), but the current error path then calls gov->exit(dbs_data) and kfree(dbs_data) again, causing a double free. Keep the direct kfree(dbs_data) for the gov->init() failure path, but after kobject_init_and_add() has been called, let kobject_put() handle the cleanup through cpufreq_dbs_data_release(). Fixes: 4ebe36c94aed ("cpufreq: Fix kobject memleak") Signed-off-by: Guangshuo Li Reviewed-by: Zhongqiu Han Acked-by: Viresh Kumar Cc: All applicable Link: https://patch.msgid.link/20260401024535.1395801-1-lgs201920130244@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_governor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index acf10187873321..86f35e45191423 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -468,13 +468,13 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) /* Failure, so roll back. */ pr_err("initialization failed (dbs_data kobject init error %d)\n", ret); - kobject_put(&dbs_data->attr_set.kobj); - policy->governor_data = NULL; if (!have_governor_per_policy()) gov->gdbs_data = NULL; - gov->exit(dbs_data); + + kobject_put(&dbs_data->attr_set.kobj); + goto free_policy_dbs_info; free_dbs_data: kfree(dbs_data); From 54ac9ff8f1196afc49d644a1625e0af1c9fcf7f5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 31 Mar 2026 13:04:23 +0200 Subject: [PATCH 297/700] arm64: Use static call trampolines when kCFI is enabled Implement arm64 support for the 'unoptimized' static call variety, which routes all calls through a trampoline that performs a tail call to the chosen function, and wire it up for use when kCFI is enabled. This works around an issue with kCFI and generic static calls, where the prototypes of default handlers such as __static_call_nop() and __static_call_ret0() don't match the expected prototype of the call site, resulting in kCFI false positives [0]. Since static call targets may be located in modules loaded out of direct branching range, this needs an ADRP/LDR pair to load the branch target into R16 and a branch-to-register (BR) instruction to perform an indirect call. Unlike on x86, there is no pressing need on arm64 to avoid indirect calls at all cost, but hiding it from the compiler as is done here does have some benefits: - the literal is located in .rodata, which gives us the same robustness advantage that code patching does; - no D-cache pollution from fetching hash values from .text sections. From an execution speed PoV, this is unlikely to make any difference at all. Cc: Sami Tolvanen Cc: Sean Christopherson Cc: Kees Cook Cc: Peter Zijlstra Cc: Will McVicker Reported-by: Carlos Llamas Closes: https://lore.kernel.org/all/20260311225822.1565895-1-cmllamas@google.com/ [0] Signed-off-by: Ard Biesheuvel Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/static_call.h | 31 ++++++++++++++++++++++++++++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/static_call.c | 23 +++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 1 + 5 files changed, 57 insertions(+) create mode 100644 arch/arm64/include/asm/static_call.h create mode 100644 arch/arm64/kernel/static_call.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 38dba5f7e4d2d7..9ea19b74b6c3c9 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -252,6 +252,7 @@ config ARM64 select HAVE_RSEQ select HAVE_RUST if RUSTC_SUPPORTS_ARM64 select HAVE_STACKPROTECTOR + select HAVE_STATIC_CALL if CFI select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES select HAVE_KRETPROBES diff --git a/arch/arm64/include/asm/static_call.h b/arch/arm64/include/asm/static_call.h new file mode 100644 index 00000000000000..30f6a046de4f24 --- /dev/null +++ b/arch/arm64/include/asm/static_call.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_STATIC_CALL_H +#define _ASM_STATIC_CALL_H + +#define __ARCH_DEFINE_STATIC_CALL_TRAMP(name, target) \ + asm(" .pushsection .static_call.text, \"ax\" \n" \ + " .align 4 \n" \ + " .globl " name " \n" \ + name ": \n" \ + " hint 34 /* BTI C */ \n" \ + " adrp x16, 1f \n" \ + " ldr x16, [x16, :lo12:1f] \n" \ + " br x16 \n" \ + " .type " name ", %function \n" \ + " .size " name ", . - " name " \n" \ + " .popsection \n" \ + " .pushsection .rodata, \"a\" \n" \ + " .align 3 \n" \ + "1: .quad " target " \n" \ + " .popsection \n") + +#define ARCH_DEFINE_STATIC_CALL_TRAMP(name, func) \ + __ARCH_DEFINE_STATIC_CALL_TRAMP(STATIC_CALL_TRAMP_STR(name), #func) + +#define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name) \ + ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0) + +#define ARCH_DEFINE_STATIC_CALL_RET0_TRAMP(name) \ + ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0) + +#endif /* _ASM_STATIC_CALL_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 76f32e424065e5..fe627100d19903 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MODULES) += module.o module-plts.o obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF) += watchdog_hld.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o +obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o obj-$(CONFIG_CPU_PM) += sleep.o suspend.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_EFI) += efi.o efi-rt-wrapper.o diff --git a/arch/arm64/kernel/static_call.c b/arch/arm64/kernel/static_call.c new file mode 100644 index 00000000000000..8b3a19e108713e --- /dev/null +++ b/arch/arm64/kernel/static_call.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +void arch_static_call_transform(void *site, void *tramp, void *func, bool tail) +{ + u64 literal; + int ret; + + if (!func) + func = __static_call_return0; + + /* decode the instructions to discover the literal address */ + literal = ALIGN_DOWN((u64)tramp + 4, SZ_4K) + + aarch64_insn_adrp_get_offset(le32_to_cpup(tramp + 4)) + + 8 * aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12, + le32_to_cpup(tramp + 8)); + + ret = aarch64_insn_write_literal_u64((void *)literal, (u64)func); + WARN_ON_ONCE(ret); +} +EXPORT_SYMBOL_GPL(arch_static_call_transform); diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index ad6133b89e7a40..af9e81f3818a7d 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -191,6 +191,7 @@ SECTIONS LOCK_TEXT KPROBES_TEXT HYPERVISOR_TEXT + STATIC_CALL_TEXT *(.gnu.warning) } From 61a11cf4812726aceaee17c96432e1c08f6ed6cb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 31 Mar 2026 07:07:47 -0600 Subject: [PATCH 298/700] io_uring: protect remaining lockless ctx->rings accesses with RCU Commit 96189080265e addressed one case of ctx->rings being potentially accessed while a resize is happening on the ring, but there are still a few others that need handling. Add a helper for retrieving the rings associated with an io_uring context, and add some sanity checking to that to catch bad uses. ->rings_rcu is always valid, as long as it's used within RCU read lock. Any use of ->rings_rcu or ->rings inside either ->uring_lock or ->completion_lock is sane as well. Do the minimum fix for the current kernel, but set it up such that this basic infra can be extended for later kernels to make this harder to mess up in the future. Thanks to Junxi Qian for finding and debugging this issue. Cc: stable@vger.kernel.org Fixes: 79cfe9e59c2a ("io_uring/register: add IORING_REGISTER_RESIZE_RINGS") Reviewed-by: Junxi Qian Tested-by: Junxi Qian Link: https://lore.kernel.org/io-uring/20260330172348.89416-1-qjx1298677004@gmail.com/ Signed-off-by: Jens Axboe --- io_uring/io_uring.c | 7 +++++-- io_uring/io_uring.h | 34 +++++++++++++++++++++++++----- io_uring/wait.c | 50 ++++++++++++++++++++++++++++----------------- io_uring/wait.h | 7 +++++-- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 20ec8fdafcae4d..48f2f627319d15 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2015,7 +2015,7 @@ int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr) if (ctx->flags & IORING_SETUP_SQ_REWIND) entries = ctx->sq_entries; else - entries = io_sqring_entries(ctx); + entries = __io_sqring_entries(ctx); entries = min(nr, entries); if (unlikely(!entries)) @@ -2250,7 +2250,9 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait) */ poll_wait(file, &ctx->poll_wq, wait); - if (!io_sqring_full(ctx)) + rcu_read_lock(); + + if (!__io_sqring_full(ctx)) mask |= EPOLLOUT | EPOLLWRNORM; /* @@ -2270,6 +2272,7 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait) if (__io_cqring_events_user(ctx) || io_has_work(ctx)) mask |= EPOLLIN | EPOLLRDNORM; + rcu_read_unlock(); return mask; } diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 0fa844faf28715..ee24bc5d77b392 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -142,16 +142,28 @@ struct io_wait_queue { #endif }; +static inline struct io_rings *io_get_rings(struct io_ring_ctx *ctx) +{ + return rcu_dereference_check(ctx->rings_rcu, + lockdep_is_held(&ctx->uring_lock) || + lockdep_is_held(&ctx->completion_lock)); +} + static inline bool io_should_wake(struct io_wait_queue *iowq) { struct io_ring_ctx *ctx = iowq->ctx; - int dist = READ_ONCE(ctx->rings->cq.tail) - (int) iowq->cq_tail; + struct io_rings *rings; + int dist; + + guard(rcu)(); + rings = io_get_rings(ctx); /* * Wake up if we have enough events, or if a timeout occurred since we * started waiting. For timeouts, we always want to return to userspace, * regardless of event count. */ + dist = READ_ONCE(rings->cq.tail) - (int) iowq->cq_tail; return dist >= 0 || atomic_read(&ctx->cq_timeouts) != iowq->nr_timeouts; } @@ -431,9 +443,9 @@ static inline void io_cqring_wake(struct io_ring_ctx *ctx) __io_wq_wake(&ctx->cq_wait); } -static inline bool io_sqring_full(struct io_ring_ctx *ctx) +static inline bool __io_sqring_full(struct io_ring_ctx *ctx) { - struct io_rings *r = ctx->rings; + struct io_rings *r = io_get_rings(ctx); /* * SQPOLL must use the actual sqring head, as using the cached_sq_head @@ -445,9 +457,15 @@ static inline bool io_sqring_full(struct io_ring_ctx *ctx) return READ_ONCE(r->sq.tail) - READ_ONCE(r->sq.head) == ctx->sq_entries; } -static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) +static inline bool io_sqring_full(struct io_ring_ctx *ctx) { - struct io_rings *rings = ctx->rings; + guard(rcu)(); + return __io_sqring_full(ctx); +} + +static inline unsigned int __io_sqring_entries(struct io_ring_ctx *ctx) +{ + struct io_rings *rings = io_get_rings(ctx); unsigned int entries; /* make sure SQ entry isn't read before tail */ @@ -455,6 +473,12 @@ static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) return min(entries, ctx->sq_entries); } +static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx) +{ + guard(rcu)(); + return __io_sqring_entries(ctx); +} + /* * Don't complete immediately but use deferred completion infrastructure. * Protected by ->uring_lock and can only be used either with diff --git a/io_uring/wait.c b/io_uring/wait.c index 0581cadf20eee5..91df86ce0d18c1 100644 --- a/io_uring/wait.c +++ b/io_uring/wait.c @@ -79,12 +79,15 @@ static enum hrtimer_restart io_cqring_min_timer_wakeup(struct hrtimer *timer) if (io_has_work(ctx)) goto out_wake; /* got events since we started waiting, min timeout is done */ - if (iowq->cq_min_tail != READ_ONCE(ctx->rings->cq.tail)) - goto out_wake; - /* if we have any events and min timeout expired, we're done */ - if (io_cqring_events(ctx)) - goto out_wake; + scoped_guard(rcu) { + struct io_rings *rings = io_get_rings(ctx); + if (iowq->cq_min_tail != READ_ONCE(rings->cq.tail)) + goto out_wake; + /* if we have any events and min timeout expired, we're done */ + if (io_cqring_events(ctx)) + goto out_wake; + } /* * If using deferred task_work running and application is waiting on * more than one request, ensure we reset it now where we are switching @@ -186,9 +189,9 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, struct ext_arg *ext_arg) { struct io_wait_queue iowq; - struct io_rings *rings = ctx->rings; + struct io_rings *rings; ktime_t start_time; - int ret; + int ret, nr_wait; min_events = min_t(int, min_events, ctx->cq_entries); @@ -201,15 +204,23 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, if (unlikely(test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq))) io_cqring_do_overflow_flush(ctx); - if (__io_cqring_events_user(ctx) >= min_events) + + rcu_read_lock(); + rings = io_get_rings(ctx); + if (__io_cqring_events_user(ctx) >= min_events) { + rcu_read_unlock(); return 0; + } init_waitqueue_func_entry(&iowq.wq, io_wake_function); iowq.wq.private = current; INIT_LIST_HEAD(&iowq.wq.entry); iowq.ctx = ctx; - iowq.cq_tail = READ_ONCE(ctx->rings->cq.head) + min_events; - iowq.cq_min_tail = READ_ONCE(ctx->rings->cq.tail); + iowq.cq_tail = READ_ONCE(rings->cq.head) + min_events; + iowq.cq_min_tail = READ_ONCE(rings->cq.tail); + nr_wait = (int) iowq.cq_tail - READ_ONCE(rings->cq.tail); + rcu_read_unlock(); + rings = NULL; iowq.nr_timeouts = atomic_read(&ctx->cq_timeouts); iowq.hit_timeout = 0; iowq.min_timeout = ext_arg->min_time; @@ -240,14 +251,6 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, trace_io_uring_cqring_wait(ctx, min_events); do { unsigned long check_cq; - int nr_wait; - - /* if min timeout has been hit, don't reset wait count */ - if (!iowq.hit_timeout) - nr_wait = (int) iowq.cq_tail - - READ_ONCE(ctx->rings->cq.tail); - else - nr_wait = 1; if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) { atomic_set(&ctx->cq_wait_nr, nr_wait); @@ -298,11 +301,20 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, break; } cond_resched(); + + /* if min timeout has been hit, don't reset wait count */ + if (!iowq.hit_timeout) + scoped_guard(rcu) + nr_wait = (int) iowq.cq_tail - + READ_ONCE(io_get_rings(ctx)->cq.tail); + else + nr_wait = 1; } while (1); if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN)) finish_wait(&ctx->cq_wait, &iowq.wq); restore_saved_sigmask_unless(ret == -EINTR); - return READ_ONCE(rings->cq.head) == READ_ONCE(rings->cq.tail) ? ret : 0; + guard(rcu)(); + return READ_ONCE(io_get_rings(ctx)->cq.head) == READ_ONCE(io_get_rings(ctx)->cq.tail) ? ret : 0; } diff --git a/io_uring/wait.h b/io_uring/wait.h index 5e236f74e1aff8..3a145fcfd3ddce 100644 --- a/io_uring/wait.h +++ b/io_uring/wait.h @@ -28,12 +28,15 @@ void io_cqring_do_overflow_flush(struct io_ring_ctx *ctx); static inline unsigned int __io_cqring_events(struct io_ring_ctx *ctx) { - return ctx->cached_cq_tail - READ_ONCE(ctx->rings->cq.head); + struct io_rings *rings = io_get_rings(ctx); + return ctx->cached_cq_tail - READ_ONCE(rings->cq.head); } static inline unsigned int __io_cqring_events_user(struct io_ring_ctx *ctx) { - return READ_ONCE(ctx->rings->cq.tail) - READ_ONCE(ctx->rings->cq.head); + struct io_rings *rings = io_get_rings(ctx); + + return READ_ONCE(rings->cq.tail) - READ_ONCE(rings->cq.head); } /* From aa35dd6bdd033dea8aa3e20cbbbe10e06b2d044f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 31 Mar 2026 08:16:58 -0600 Subject: [PATCH 299/700] io_uring/bpf_filters: retain COW'ed settings on parse failures If io_parse_restrictions() fails, it ends up clearing any restrictions currently set. The intent is only to clear whatever it already applied, but it ends up clearing everything, including whatever settings may have been applied in a copy-on-write fashion already. Ensure that those are retained. Link: https://lore.kernel.org/io-uring/CAK8a0jzF-zaO5ZmdOrmfuxrhXuKg5m5+RDuO7tNvtj=kUYbW7Q@mail.gmail.com/ Reported-by: antonius Fixes: ed82f35b926b ("io_uring: allow registration of per-task restrictions") Signed-off-by: Jens Axboe --- io_uring/register.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/io_uring/register.c b/io_uring/register.c index 5f3eb018fb3285..837324bf022335 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -178,9 +178,17 @@ static __cold int io_register_restrictions(struct io_ring_ctx *ctx, return -EBUSY; ret = io_parse_restrictions(arg, nr_args, &ctx->restrictions); - /* Reset all restrictions if an error happened */ + /* + * Reset all restrictions if an error happened, but retain any COW'ed + * settings. + */ if (ret < 0) { + struct io_bpf_filters *bpf = ctx->restrictions.bpf_filters; + bool cowed = ctx->restrictions.bpf_filters_cow; + memset(&ctx->restrictions, 0, sizeof(ctx->restrictions)); + ctx->restrictions.bpf_filters = bpf; + ctx->restrictions.bpf_filters_cow = cowed; return ret; } if (ctx->restrictions.op_registered) From cffff6df669a438ecac506dadd49a53d4475a796 Mon Sep 17 00:00:00 2001 From: Corey Hickey Date: Tue, 31 Mar 2026 14:49:06 -0700 Subject: [PATCH 300/700] hwmon: (asus-ec-sensors) Fix T_Sensor for PRIME X670E-PRO WIFI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Asus PRIME X670E-PRO WIFI, the driver reports a constant value of zero for T_Sensor. On this board, the register for T_Sensor is at a different address, as found by experimentation and confirmed by comparison to an independent temperature reading. * sensor disconnected: -62.0°C * ambient temperature: +22.0°C * held between fingers: +30.0°C Introduce SENSOR_TEMP_T_SENSOR_ALT1 to support the PRIME X670E-PRO WIFI without causing a regression for other 600-series boards Fixes: e0444758dd1b ("hwmon: (asus-ec-sensors) add PRIME X670E-PRO WIFI") Signed-off-by: Corey Hickey Link: https://lore.kernel.org/r/20260331215414.368785-1-bugfood-ml@fatooh.org [groeck: Fixed typo, updated Fixes: reference] Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 86f444498650e5..adedaf0db10e61 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -111,6 +111,8 @@ enum ec_sensors { ec_sensor_temp_mb, /* "T_Sensor" temperature sensor reading [℃] */ ec_sensor_temp_t_sensor, + /* like ec_sensor_temp_t_sensor, but at an alternate address [℃] */ + ec_sensor_temp_t_sensor_alt1, /* VRM temperature [℃] */ ec_sensor_temp_vrm, /* VRM east (right) temperature [℃] */ @@ -160,6 +162,7 @@ enum ec_sensors { #define SENSOR_TEMP_CPU_PACKAGE BIT(ec_sensor_temp_cpu_package) #define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb) #define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor) +#define SENSOR_TEMP_T_SENSOR_ALT1 BIT(ec_sensor_temp_t_sensor_alt1) #define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm) #define SENSOR_TEMP_VRME BIT(ec_sensor_temp_vrme) #define SENSOR_TEMP_VRMW BIT(ec_sensor_temp_vrmw) @@ -279,6 +282,8 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_temp_t_sensor] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36), + [ec_sensor_temp_t_sensor_alt1] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x37), [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), [ec_sensor_temp_water_in] = @@ -519,7 +524,7 @@ static const struct ec_board_info board_info_prime_x570_pro = { static const struct ec_board_info board_info_prime_x670e_pro_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM | - SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + SENSOR_TEMP_T_SENSOR_ALT1 | SENSOR_FAN_CPU_OPT, .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, .family = family_amd_600_series, }; From 328335a79487ec38d6b0e1aa807785b0f75e594d Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Tue, 31 Mar 2026 17:30:22 -0500 Subject: [PATCH 301/700] powerpc/powernv/iommu: iommu incorrectly bypass DMA APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a PowerNV environment, for devices that supports DMA mask less than 64 bit but larger than 32 bits, iommu is incorrectly bypassing DMA APIs while allocating and mapping buffers for DMA operations. Devices are failing with ENOMEN during probe with the following messages amdgpu 0000:01:00.0: [drm] Detected VRAM RAM=4096M, BAR=4096M amdgpu 0000:01:00.0: [drm] RAM width 128bits GDDR5 amdgpu 0000:01:00.0: iommu: 64-bit OK but direct DMA is limited by 0 amdgpu 0000:01:00.0: dma_iommu_get_required_mask: returning bypass mask 0xfffffffffffffff amdgpu 0000:01:00.0: 4096M of VRAM memory ready amdgpu 0000:01:00.0: 32570M of GTT memory ready. amdgpu 0000:01:00.0: (-12) failed to allocate kernel bo amdgpu 0000:01:00.0: [drm] Debug VRAM access will use slowpath MM access amdgpu 0000:01:00.0: [drm] GART: num cpu pages 4096, num gpu pages 65536 amdgpu 0000:01:00.0: [drm] PCIE GART of 256M enabled (table at 0x000000F4FFF80000). amdgpu 0000:01:00.0: (-12) failed to allocate kernel bo amdgpu 0000:01:00.0: (-12) create WB bo failed amdgpu 0000:01:00.0: amdgpu_device_wb_init failed -12 amdgpu 0000:01:00.0: amdgpu_device_ip_init failed amdgpu 0000:01:00.0: Fatal error during GPU init amdgpu 0000:01:00.0: finishing device. amdgpu 0000:01:00.0: probe with driver amdgpu failed with error -12 amdgpu 0000:01:00.0: ttm finalized Fixes: 1471c517cf7d ("powerpc/iommu: bypass DMA APIs for coherent allocations for pre-mapped memory") Suggested-by: Ritesh Harjani (IBM) Reviewed-by: Ritesh Harjani (IBM) Reported-by: Dan Horák Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5039 Tested-by: Dan Horak Closes: https://lore.kernel.org/linuxppc-dev/20260313142351.609bc4c3efe1184f64ca5f44@danny.cz/ Signed-off-by: Gaurav Batra Closes: https://lore.kernel.org/linuxppc-dev/20260313142351.609bc4c3efe1184f64ca5f44@danny.cz/ [Maddy: Fixed tags] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260331223022.47488-1-gbatra@linux.ibm.com --- arch/powerpc/kernel/dma-iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 73e10bd4d56db5..8b4de508d2eb10 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -67,7 +67,7 @@ bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg, } bool arch_dma_alloc_direct(struct device *dev) { - if (dev->dma_ops_bypass) + if (dev->dma_ops_bypass && dev->bus_dma_limit) return true; return false; @@ -75,7 +75,7 @@ bool arch_dma_alloc_direct(struct device *dev) bool arch_dma_free_direct(struct device *dev, dma_addr_t dma_handle) { - if (!dev->dma_ops_bypass) + if (!dev->dma_ops_bypass || !dev->bus_dma_limit) return false; return is_direct_handle(dev, dma_handle); From c7661bfc7422443df394c01e069ae4e5c3a7f04c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:16 +0800 Subject: [PATCH 302/700] ASoC: fsl_micfil: Add access property for "VAD Detected" Add access property SNDRV_CTL_ELEM_ACCESS_READ for control "VAD Detected", which doesn't support put operation, otherwise there will be issue with mixer-test. Fixes: 29dbfeecab85 ("ASoC: fsl_micfil: Add Hardware Voice Activity Detector support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index d6cde2757c6d55..79850211742cb3 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -503,7 +503,13 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), SOC_SINGLE("HWVAD ZCD And Behavior Switch", REG_MICFIL_VAD0_ZCD, 4, 1, 0), - SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "VAD Detected", + .info = snd_soc_info_bool_ext, + .get = hwvad_detected, + }, }; static int fsl_micfil_use_verid(struct device *dev) From 59b9061824f2179fe133e2636203548eaba3e528 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:17 +0800 Subject: [PATCH 303/700] ASoC: fsl_micfil: Fix event generation in hwvad_put_enable() ALSA controls should return 1 if the value in the control changed but the control put operation hwvad_put_enable() only returns 0 or a negative error code, causing ALSA to not generate any change events. Add a suitable check in the function before updating the vad_enabled variable. Fixes: 29dbfeecab85 ("ASoC: fsl_micfil: Add Hardware Voice Activity Detector support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 79850211742cb3..97f24c9bdd68b8 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -377,10 +377,15 @@ static int hwvad_put_enable(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); int val = snd_soc_enum_item_to_val(e, item[0]); + bool change = false; + if (val < 0 || val > 1) + return -EINVAL; + + change = (micfil->vad_enabled != val); micfil->vad_enabled = val; - return 0; + return change; } static int hwvad_get_enable(struct snd_kcontrol *kcontrol, From 7e226209906906421f0d952d7304e48fdb0adabc Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:18 +0800 Subject: [PATCH 304/700] ASoC: fsl_micfil: Fix event generation in hwvad_put_init_mode() ALSA controls should return 1 if the value in the control changed but the control put operation hwvad_put_init_mode() only returns 0 or a negative error code, causing ALSA to not generate any change events. Add a suitable check in the function before updating the vad_init_mode variable. Fixes: 29dbfeecab85 ("ASoC: fsl_micfil: Add Hardware Voice Activity Detector support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-4-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 97f24c9bdd68b8..1c826e0cb1d5e8 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -407,13 +407,18 @@ static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); int val = snd_soc_enum_item_to_val(e, item[0]); + bool change = false; + + if (val < MICFIL_HWVAD_ENVELOPE_MODE || val > MICFIL_HWVAD_ENERGY_MODE) + return -EINVAL; /* 0 - Envelope-based Mode * 1 - Energy-based Mode */ + change = (micfil->vad_init_mode != val); micfil->vad_init_mode = val; - return 0; + return change; } static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, From fc4daaddb276d370b7da3819872044df446a1911 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:19 +0800 Subject: [PATCH 305/700] ASoC: fsl_micfil: Fix event generation in micfil_range_set() ALSA controls should return 1 if the value in the control changed but the control put operation micfil_range_set() only returns 0 or a negative error code, causing ALSA to not generate any change events. Use snd_soc_component_update_bits() function to replace the regmap_update_bits(), for snd_soc_component_update_bits() has the capability of return check status. Also enable pm runtime before calling the function snd_soc_component_update_bits() to make the regmap cache data align with the value in hardware. Fixes: ef1a7e02fdb7 ("ASoC: fsl_micfil: Set channel range control") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-5-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 1c826e0cb1d5e8..0cfdd6343291ac 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -210,15 +210,23 @@ static int micfil_range_set(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int shift = mc->shift; int max_range, new_range; + int ret; new_range = ucontrol->value.integer.value[0]; max_range = micfil_get_max_range(micfil); if (new_range > max_range) dev_warn(&micfil->pdev->dev, "range makes channel %d data unreliable\n", shift / 4); - regmap_update_bits(micfil->regmap, REG_MICFIL_OUT_CTRL, 0xF << shift, new_range << shift); + ret = pm_runtime_resume_and_get(cmpnt->dev); + if (ret) + return ret; - return 0; + ret = snd_soc_component_update_bits(cmpnt, REG_MICFIL_OUT_CTRL, 0xF << shift, + new_range << shift); + + pm_runtime_put_autosuspend(cmpnt->dev); + + return ret; } static int micfil_set_quality(struct fsl_micfil *micfil) From 7d2bd35100de370dc326b250e8f6b66bee06a2f3 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:20 +0800 Subject: [PATCH 306/700] ASoC: fsl_micfil: Fix event generation in micfil_put_dc_remover_state() ALSA controls should return 1 if the value in the control changed but the control put operation micfil_put_dc_remover_state() only returns 0 or a negative error code, causing ALSA to not generate any change events. return the value of snd_soc_component_update_bits() directly, as it has the capability of return check status of changed or not. Also enable pm runtime before calling the function snd_soc_component_update_bits() to make the regmap cache data align with the value in hardware. Fixes: 29dbfeecab85 ("ASoC: fsl_micfil: Add Hardware Voice Activity Detector support") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-6-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 0cfdd6343291ac..983805bbaae272 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -351,6 +351,10 @@ static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, if (val < 0 || val > 3) return -EINVAL; + ret = pm_runtime_resume_and_get(comp->dev); + if (ret) + return ret; + micfil->dc_remover = val; /* Calculate total value for all channels */ @@ -360,10 +364,10 @@ static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, /* Update DC Remover mode for all channels */ ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, MICFIL_DC_CTRL_CONFIG, reg_val); - if (ret < 0) - return ret; - return 0; + pm_runtime_put_autosuspend(comp->dev); + + return ret; } static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, From e5785093b1b45af7ee57d18619b2854a8aed073a Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:21 +0800 Subject: [PATCH 307/700] ASoC: fsl_micfil: Fix event generation in micfil_quality_set() ALSA controls should return 1 if the value in the control changed but the control put operation micfil_quality_set() only returns 0 or a negative error code, causing ALSA to not generate any change events. Add a suitable check in the function before updating the quality variable. Also enable pm runtime before calling the function micfil_set_quality() to make the regmap cache data align with the value in hardware. Fixes: bea1d61d5892 ("ASoC: fsl_micfil: rework quality setting") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-7-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 983805bbaae272..2e887f1f1f361e 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -289,10 +289,34 @@ static int micfil_quality_set(struct snd_kcontrol *kcontrol, { struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); + int val = ucontrol->value.integer.value[0]; + bool change = false; + int old_val; + int ret; + + if (val < QUALITY_HIGH || val > QUALITY_VLOW2) + return -EINVAL; + + if (micfil->quality != val) { + ret = pm_runtime_resume_and_get(cmpnt->dev); + if (ret) + return ret; + + old_val = micfil->quality; + micfil->quality = val; + ret = micfil_set_quality(micfil); - micfil->quality = ucontrol->value.integer.value[0]; + pm_runtime_put_autosuspend(cmpnt->dev); - return micfil_set_quality(micfil); + if (ret) { + micfil->quality = old_val; + return ret; + } + + change = true; + } + + return change; } static const char * const micfil_hwvad_enable[] = { From 1b61c8103c9317a9c37fe544c2d83cee1c281149 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:22 +0800 Subject: [PATCH 308/700] ASoC: fsl_xcvr: Fix event generation in fsl_xcvr_arc_mode_put() ALSA controls should return 1 if the value in the control changed but the control put operation fsl_xcvr_arc_mode_put() only returns 0 or a negative error code, causing ALSA to not generate any change events. Add a suitable check in the function before updating the arc_mode variable. Fixes: 28564486866f ("ASoC: fsl_xcvr: Add XCVR ASoC CPU DAI driver") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-8-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index a268fb81a2f868..008e45009c83f5 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -115,10 +115,17 @@ static int fsl_xcvr_arc_mode_put(struct snd_kcontrol *kcontrol, struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; + int val = snd_soc_enum_item_to_val(e, item[0]); + int ret; - xcvr->arc_mode = snd_soc_enum_item_to_val(e, item[0]); + if (val < 0 || val > 1) + return -EINVAL; - return 0; + ret = (xcvr->arc_mode != val); + + xcvr->arc_mode = val; + + return ret; } static int fsl_xcvr_arc_mode_get(struct snd_kcontrol *kcontrol, From 64a496ba976324615b845d60739dfcdae3d57434 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:23 +0800 Subject: [PATCH 309/700] ASoC: fsl_xcvr: Fix event generation in fsl_xcvr_mode_put() ALSA controls should return 1 if the value in the control changed but the control put operation fsl_xcvr_mode_put() only returns 0 or a negative error code, causing ALSA to not generate any change events. Add a suitable check in the function before updating the mode variable. Fixes: 28564486866f ("ASoC: fsl_xcvr: Add XCVR ASoC CPU DAI driver") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-9-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 008e45009c83f5..d7a823384c08ae 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -225,10 +225,17 @@ static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; + int val = snd_soc_enum_item_to_val(e, item[0]); struct snd_soc_card *card = dai->component->card; struct snd_soc_pcm_runtime *rtd; + int ret; + + if (val < FSL_XCVR_MODE_SPDIF || val > FSL_XCVR_MODE_EARC) + return -EINVAL; - xcvr->mode = snd_soc_enum_item_to_val(e, item[0]); + ret = (xcvr->mode != val); + + xcvr->mode = val; fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, (xcvr->mode == FSL_XCVR_MODE_ARC)); @@ -238,7 +245,7 @@ static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, rtd = snd_soc_get_pcm_runtime(card, card->dai_link); rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count = (xcvr->mode == FSL_XCVR_MODE_SPDIF ? 1 : 0); - return 0; + return ret; } static int fsl_xcvr_mode_get(struct snd_kcontrol *kcontrol, From 00541b86fb578d4949cfdd6aff1f82d43fcf07af Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:24 +0800 Subject: [PATCH 310/700] ASoC: fsl_easrc: Check the variable range in fsl_easrc_iec958_put_bits() Add check of input value's range in fsl_easrc_iec958_put_bits(), otherwise the wrong value may be written from user space. Fixes: 955ac624058f ("ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-10-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 6c56134c60cc81..6de1e1d3d8dc8d 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -54,6 +54,9 @@ static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol, unsigned int regval = ucontrol->value.integer.value[0]; int ret; + if (regval < EASRC_WIDTH_16_BIT || regval > EASRC_WIDTH_24_BIT) + return -EINVAL; + ret = (easrc_priv->bps_iec958[mc->regbase] != regval); easrc_priv->bps_iec958[mc->regbase] = regval; From aa21fe4a81458cf469c2615b08cbde5997dde25a Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:25 +0800 Subject: [PATCH 311/700] ASoC: fsl_easrc: Fix value type in fsl_easrc_iec958_get_bits() The value type of controls "Context 0 IEC958 Bits Per Sample" should be integer, not enumerated, the issue is found by the mixer-test. Fixes: 955ac624058f ("ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-11-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 6de1e1d3d8dc8d..fbd1ba12aad0d5 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -73,7 +73,7 @@ static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol, struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - ucontrol->value.enumerated.item[0] = easrc_priv->bps_iec958[mc->regbase]; + ucontrol->value.integer.value[0] = easrc_priv->bps_iec958[mc->regbase]; return 0; } From 47f28a5bd154a95d5aa563dde02a801bd32ddb81 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 1 Apr 2026 17:42:26 +0800 Subject: [PATCH 312/700] ASoC: fsl_easrc: Change the type for iec958 channel status controls Use the type SNDRV_CTL_ELEM_TYPE_IEC958 for iec958 channel status controls, the original type will cause mixer-test to iterate all 32bit values, which costs a lot of time. And using IEC958 type can reduce the control numbers. Also enable pm runtime before updating registers to make the regmap cache data align with the value in hardware. Fixes: 955ac624058f ("ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260401094226.2900532-12-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 118 +++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 34 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index fbd1ba12aad0d5..41985162df0b0a 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -78,17 +78,47 @@ static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol, return 0; } +static int fsl_easrc_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + static int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - unsigned int regval; + struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component); + unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status; + int ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS0(mc->regbase), ®val[0]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS1(mc->regbase), ®val[1]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS2(mc->regbase), ®val[2]); + if (ret) + return ret; - regval = snd_soc_component_read(component, mc->regbase); + ret = regmap_read(easrc->regmap, REG_EASRC_CS3(mc->regbase), ®val[3]); + if (ret) + return ret; + + ret = regmap_read(easrc->regmap, REG_EASRC_CS4(mc->regbase), ®val[4]); + if (ret) + return ret; - ucontrol->value.integer.value[0] = regval; + ret = regmap_read(easrc->regmap, REG_EASRC_CS5(mc->regbase), ®val[5]); + if (ret) + return ret; return 0; } @@ -100,22 +130,62 @@ static int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol, struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; struct fsl_asrc *easrc = snd_soc_component_get_drvdata(component); - unsigned int regval = ucontrol->value.integer.value[0]; - bool changed; + unsigned int *regval = (unsigned int *)ucontrol->value.iec958.status; + bool changed, changed_all = false; int ret; - ret = regmap_update_bits_check(easrc->regmap, mc->regbase, - GENMASK(31, 0), regval, &changed); - if (ret != 0) + ret = pm_runtime_resume_and_get(component->dev); + if (ret) return ret; - return changed; + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS0(mc->regbase), + GENMASK(31, 0), regval[0], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS1(mc->regbase), + GENMASK(31, 0), regval[1], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS2(mc->regbase), + GENMASK(31, 0), regval[2], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS3(mc->regbase), + GENMASK(31, 0), regval[3], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS4(mc->regbase), + GENMASK(31, 0), regval[4], &changed); + if (ret != 0) + goto err; + changed_all |= changed; + + ret = regmap_update_bits_check(easrc->regmap, REG_EASRC_CS5(mc->regbase), + GENMASK(31, 0), regval[5], &changed); + if (ret != 0) + goto err; + changed_all |= changed; +err: + pm_runtime_put_autosuspend(component->dev); + + if (ret != 0) + return ret; + else + return changed_all; } #define SOC_SINGLE_REG_RW(xname, xreg) \ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \ + .info = fsl_easrc_iec958_info, .get = fsl_easrc_get_reg, \ .put = fsl_easrc_set_reg, \ .private_value = (unsigned long)&(struct soc_mreg_control) \ { .regbase = xreg, .regcount = 1, .nbits = 32, \ @@ -146,30 +216,10 @@ static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = { SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2), SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)), - SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)), - SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)), - SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)), - SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)), + SOC_SINGLE_REG_RW("Context 0 IEC958 CS", 0), + SOC_SINGLE_REG_RW("Context 1 IEC958 CS", 1), + SOC_SINGLE_REG_RW("Context 2 IEC958 CS", 2), + SOC_SINGLE_REG_RW("Context 3 IEC958 CS", 3), }; /* From 35d8945bf9b0c4d9586c7fa9fadeecf2cfc26c23 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Mon, 16 Mar 2026 01:28:22 +0800 Subject: [PATCH 313/700] MIPS: Loongson64: env: Check UARTs passed by LEFI cautiously Some firmware does not set nr_uarts properly and passes empty items. Iterate at most min(system->nr_uarts, MAX_UARTS) items to prevent out-of-bounds access, and ignore UARTs with addr 0 silently. Meanwhile, our DT only works with UPIO_MEM but theoretically firmware may pass other IO types, so explicitly check against that. Tested on Loongson-LS3A4000-7A1000-NUC-SE. Fixes: 3989ed418483 ("MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI") Cc: stable@vger.kernel.org Reviewed-by: Yao Zi Signed-off-by: Rong Zhang Reviewed-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/loongson64/env.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index 11ddf02d6a1586..7abcca7ab4ed44 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -106,9 +108,23 @@ static void __init lefi_fixup_fdt(struct system_loongson *system) is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G; - for (i = 0; i < system->nr_uarts; i++) { + for (i = 0; i < min(system->nr_uarts, MAX_UARTS); i++) { uartdev = &system->uarts[i]; + /* + * Some firmware does not set nr_uarts properly and passes empty + * items. Ignore them silently. + */ + if (uartdev->uart_base == 0) + continue; + + /* Our DT only works with UPIO_MEM. */ + if (uartdev->iotype != UPIO_MEM) { + pr_warn("Ignore UART 0x%llx with iotype %u passed by firmware\n", + uartdev->uart_base, uartdev->iotype); + continue; + } + ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base, uartdev->uartclk); /* From 43985a62bab9d35e5e9af41118ce2f44c01b97d2 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Tue, 24 Feb 2026 10:22:50 +0800 Subject: [PATCH 314/700] mips: ralink: update CPU clock index Update CPU clock index to match the clock driver changes. Fixes: d34db686a3d7 ("clk: ralink: mtmips: fix clocks probe order in oldest ralink SoCs") Signed-off-by: Mieczyslaw Nalewaj Signed-off-by: Shiji Yang Reviewed-by: Sergio Paracuellos Signed-off-by: Thomas Bogendoerfer --- arch/mips/ralink/clk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c index 9db73fcac522eb..5c1eb46ef5d07e 100644 --- a/arch/mips/ralink/clk.c +++ b/arch/mips/ralink/clk.c @@ -21,16 +21,16 @@ static const char *clk_cpu(int *idx) { switch (ralink_soc) { case RT2880_SOC: - *idx = 0; + *idx = 1; return "ralink,rt2880-sysc"; case RT3883_SOC: - *idx = 0; + *idx = 1; return "ralink,rt3883-sysc"; case RT305X_SOC_RT3050: - *idx = 0; + *idx = 1; return "ralink,rt3050-sysc"; case RT305X_SOC_RT3052: - *idx = 0; + *idx = 1; return "ralink,rt3052-sysc"; case RT305X_SOC_RT3350: *idx = 1; From d62cf1511743526f530a4c169424e50c757f5a5e Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 11:38:06 +0000 Subject: [PATCH 315/700] MIPS: SiByte: Bring back cache initialisation Bring back cache initialisation for Broadcom SiByte SB1 cores, which has been removed causing the kernel to hang at bootstrap right after: Dentry cache hash table entries: 524288 (order: 8, 4194304 bytes, linear) Inode-cache hash table entries: 262144 (order: 7, 2097152 bytes, linear) The cause of the problem is R4k cache handlers are also used by Broadcom SiByte SB1 cores, however with a different cache error exception handler and therefore not using CPU_R4K_CACHE_TLB: obj-$(CONFIG_CPU_R4K_CACHE_TLB) += c-r4k.o cex-gen.o tlb-r4k.o obj-$(CONFIG_CPU_SB1) += c-r4k.o cerr-sb1.o cex-sb1.o tlb-r4k.o (from arch/mips/mm/Makefile). Fixes: bbe4f634f48c ("mips: fix r3k_cache_init build regression") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v6.8+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index e3b4224c9a4061..ad9b0430a28e2d 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -207,7 +207,8 @@ void cpu_cache_init(void) { if (IS_ENABLED(CONFIG_CPU_R3000) && cpu_has_3k_cache) r3k_cache_init(); - if (IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) && cpu_has_4k_cache) + if ((IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) || + IS_ENABLED(CONFIG_CPU_SB1)) && cpu_has_4k_cache) r4k_cache_init(); if (IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON) && cpu_has_octeon_cache) From ec8bf18814915460d9c617b556bf024efef26613 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 30 Mar 2026 02:54:09 +0100 Subject: [PATCH 316/700] MIPS: Fix the GCC version check for `__multi3' workaround It was only GCC 10 that fixed a MIPS64r6 code generation issue with a `__multi3' libcall inefficiently produced to perform 64-bit widening multiplication while suitable machine instructions exist to do such a calculation. The fix went in with GCC commit 48b2123f6336 ("re PR target/82981 (unnecessary __multi3 call for mips64r6 linux kernel)"). Adjust our code accordingly, removing build failures such as: mips64-linux-ld: lib/math/div64.o: in function `mul_u64_add_u64_div_u64': div64.c:(.text+0x84): undefined reference to `__multi3' with the GCC versions affected. Fixes: ebabcf17bcd7 ("MIPS: Implement __multi3 for GCC7 MIPS64r6 builds") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601140146.hMLODc6v-lkp@intel.com/ Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v4.15+ Reviewed-by: David Laight --- arch/mips/lib/multi3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c index 4c2483f410c26b..92b3778bb56feb 100644 --- a/arch/mips/lib/multi3.c +++ b/arch/mips/lib/multi3.c @@ -4,12 +4,12 @@ #include "libgcc.h" /* - * GCC 7 & older can suboptimally generate __multi3 calls for mips64r6, so for + * GCC 9 & older can suboptimally generate __multi3 calls for mips64r6, so for * that specific case only we implement that intrinsic here. * * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981 */ -#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 8) +#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 10) /* multiply 64-bit values, low 64-bits returned */ static inline long long notrace dmulu(long long a, long long b) @@ -51,4 +51,4 @@ ti_type notrace __multi3(ti_type a, ti_type b) } EXPORT_SYMBOL(__multi3); -#endif /* 64BIT && CPU_MIPSR6 && GCC7 */ +#endif /* 64BIT && CPU_MIPSR6 && GCC9 */ From 8374c2cb83b95b3c92f129fd56527225c20a058c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:18 +0000 Subject: [PATCH 317/700] MIPS: Always record SEGBITS in cpu_data.vmbits With a 32-bit kernel running on 64-bit MIPS hardware the hardcoded value of `cpu_vmbits' only records the size of compatibility useg and does not reflect the size of native xuseg or the complete range of values allowed in the VPN2 field of TLB entries. An upcoming change will need the actual VPN2 value range permitted even in 32-bit kernel configurations, so always include the `vmbits' member in `struct cpuinfo_mips' and probe for SEGBITS when running on 64-bit hardware and resorting to the currently hardcoded value of 31 on 32-bit processors. No functional change for users of `cpu_vmbits'. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/cpu-features.h | 1 - arch/mips/include/asm/cpu-info.h | 2 -- arch/mips/include/asm/mipsregs.h | 2 ++ arch/mips/kernel/cpu-probe.c | 13 ++++++++----- arch/mips/kernel/cpu-r3k-probe.c | 2 ++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 404390bb87eaf5..3f11e5218e6c65 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -484,7 +484,6 @@ # endif # ifndef cpu_vmbits # define cpu_vmbits cpu_data[0].vmbits -# define __NEED_VMBITS_PROBE # endif #endif diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index fd60837ce50bc0..211b578af6aa0a 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -80,9 +80,7 @@ struct cpuinfo_mips { int srsets; /* Shadow register sets */ int package;/* physical package number */ unsigned int globalnumber; -#ifdef CONFIG_64BIT int vmbits; /* Virtual memory size in bits */ -#endif void *data; /* Additional data */ unsigned int watch_reg_count; /* Number that exist */ unsigned int watch_reg_use_cnt; /* Usable by ptrace */ diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index f799c0d723dac2..12a095dbf9e2a9 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -1871,6 +1871,8 @@ do { \ #define read_c0_entryhi() __read_ulong_c0_register($10, 0) #define write_c0_entryhi(val) __write_ulong_c0_register($10, 0, val) +#define read_c0_entryhi_64() __read_64bit_c0_register($10, 0) +#define write_c0_entryhi_64(val) __write_64bit_c0_register($10, 0, val) #define read_c0_guestctl1() __read_32bit_c0_register($10, 4) #define write_c0_guestctl1(val) __write_32bit_c0_register($10, 4, val) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 1e49e05ac8b1c4..489612ed9d4987 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -210,11 +210,14 @@ static inline void set_elf_base_platform(const char *plat) static inline void cpu_probe_vmbits(struct cpuinfo_mips *c) { -#ifdef __NEED_VMBITS_PROBE - write_c0_entryhi(0x3fffffffffffe000ULL); - back_to_back_c0_hazard(); - c->vmbits = fls64(read_c0_entryhi() & 0x3fffffffffffe000ULL); -#endif + int vmbits = 31; + + if (cpu_has_64bits) { + write_c0_entryhi_64(0x3fffffffffffe000ULL); + back_to_back_c0_hazard(); + vmbits = fls64(read_c0_entryhi_64() & 0x3fffffffffffe000ULL); + } + c->vmbits = vmbits; } static void set_isa(struct cpuinfo_mips *c, unsigned int isa) diff --git a/arch/mips/kernel/cpu-r3k-probe.c b/arch/mips/kernel/cpu-r3k-probe.c index 0c826f729f7527..edcf04de0a6fbf 100644 --- a/arch/mips/kernel/cpu-r3k-probe.c +++ b/arch/mips/kernel/cpu-r3k-probe.c @@ -137,6 +137,8 @@ void cpu_probe(void) else cpu_set_nofpu_opts(c); + c->vmbits = 31; + reserve_exception_space(0, 0x400); } From 74283cfe216392c7b776ebf6045b5b15ed9dffcd Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:23 +0000 Subject: [PATCH 318/700] MIPS: mm: Suppress TLB uniquification on EHINV hardware Hardware that supports the EHINV feature, mandatory for R6 ISA and FTLB implementation, lets software mark TLB entries invalid, which eliminates the need to ensure no duplicate matching entries are ever created. This feature is already used by local_flush_tlb_all(), via the UNIQUE_ENTRYHI macro, making the preceding call to r4k_tlb_uniquify() superfluous. The next change will also modify uniquification code such that it'll become incompatible with the FTLB and MMID features, as well as MIPSr6 CPUs that do not implement 4KiB pages. Therefore prevent r4k_tlb_uniquify() from being used on EHINV hardware, as denoted by `cpu_has_tlbinv'. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 44a662536148e2..27bb6446960475 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -640,7 +640,8 @@ static void r4k_tlb_configure(void) temp_tlb_entry = current_cpu_data.tlbsize - 1; /* From this point on the ARC firmware is dead. */ - r4k_tlb_uniquify(); + if (!cpu_has_tlbinv) + r4k_tlb_uniquify(); local_flush_tlb_all(); /* Did I tell you that ARC SUCKS? */ From 540760b77b8fc49d39d1b2b76196e5ec57711a32 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 27 Mar 2026 18:57:30 +0000 Subject: [PATCH 319/700] MIPS: mm: Rewrite TLB uniquification for the hidden bit feature Before the introduction of the EHINV feature, which lets software mark TLB entries invalid, certain older implementations of the MIPS ISA were equipped with an analogous bit, as a vendor extension, which however is hidden from software and only ever set at reset, and then any software write clears it, making the intended TLB entry valid. This feature makes it unsafe to read a TLB entry with TLBR, modify the page mask, and write the entry back with TLBWI, because this operation will implicitly clear the hidden bit and this may create a duplicate entry, as with the presence of the hidden bit there is no guarantee all the entries across the TLB are unique each. Usually the firmware has already uniquified TLB entries before handing control over, in which case we only need to guarantee at bootstrap no clash will happen with the VPN2 values chosen in local_flush_tlb_all(). However with systems such as Mikrotik RB532 we get handed the TLB as at reset, with the hidden bit set across the entries and possibly duplicate entries present. This then causes a machine check exception when page sizes are reset in r4k_tlb_uniquify() and prevents the system from booting. Rewrite the algorithm used in r4k_tlb_uniquify() then such as to avoid the reuse of ASID/VPN values across the TLB. Get rid of global entries first as they may be blocking the entire address space, e.g. 16 256MiB pages will exhaust the whole address space of a 32-bit CPU and a single big page can exhaust the 32-bit compatibility space on a 64-bit CPU. Details of the algorithm chosen are given across the code itself. Fixes: 9f048fa48740 ("MIPS: mm: Prevent a TLB shutdown on initial uniquification") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 282 +++++++++++++++++++++++++++++++++-------- 1 file changed, 228 insertions(+), 54 deletions(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 27bb6446960475..08c3e65bf0f29d 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -511,87 +513,259 @@ static int __init set_ntlb(char *str) __setup("ntlb=", set_ntlb); -/* Comparison function for EntryHi VPN fields. */ -static int r4k_vpn_cmp(const void *a, const void *b) +/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */ +#define VPN2_SHIFT 13 + +/* Read full EntryHi even with CONFIG_32BIT. */ +static inline unsigned long long read_c0_entryhi_native(void) +{ + return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi(); +} + +/* Write full EntryHi even with CONFIG_32BIT. */ +static inline void write_c0_entryhi_native(unsigned long long v) { - long v = *(unsigned long *)a - *(unsigned long *)b; - int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; - return s ? (v != 0) | v >> s : v; + if (cpu_has_64bits) + write_c0_entryhi_64(v); + else + write_c0_entryhi(v); } +/* TLB entry state for uniquification. */ +struct tlbent { + unsigned long long wired:1; + unsigned long long global:1; + unsigned long long asid:10; + unsigned long long vpn:51; + unsigned long long pagesz:5; + unsigned long long index:14; +}; + /* - * Initialise all TLB entries with unique values that do not clash with - * what we have been handed over and what we'll be using ourselves. + * Comparison function for TLB entry sorting. Place wired entries first, + * then global entries, then order by the increasing VPN/ASID and the + * decreasing page size. This lets us avoid clashes with wired entries + * easily and get entries for larger pages out of the way first. + * + * We could group bits so as to reduce the number of comparisons, but this + * is seldom executed and not performance-critical, so prefer legibility. */ -static void __ref r4k_tlb_uniquify(void) +static int r4k_entry_cmp(const void *a, const void *b) { - int tlbsize = current_cpu_data.tlbsize; - bool use_slab = slab_is_available(); - int start = num_wired_entries(); - phys_addr_t tlb_vpn_size; - unsigned long *tlb_vpns; - unsigned long vpn_mask; - int cnt, ent, idx, i; - - vpn_mask = GENMASK(cpu_vmbits - 1, 13); - vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; + struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b; + + if (ea.wired > eb.wired) + return -1; + else if (ea.wired < eb.wired) + return 1; + else if (ea.global > eb.global) + return -1; + else if (ea.global < eb.global) + return 1; + else if (ea.vpn < eb.vpn) + return -1; + else if (ea.vpn > eb.vpn) + return 1; + else if (ea.asid < eb.asid) + return -1; + else if (ea.asid > eb.asid) + return 1; + else if (ea.pagesz > eb.pagesz) + return -1; + else if (ea.pagesz < eb.pagesz) + return 1; + else + return 0; +} - tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); - tlb_vpns = (use_slab ? - kmalloc(tlb_vpn_size, GFP_KERNEL) : - memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns))); - if (WARN_ON(!tlb_vpns)) - return; /* Pray local_flush_tlb_all() is good enough. */ +/* + * Fetch all the TLB entries. Mask individual VPN values retrieved with + * the corresponding page mask and ignoring any 1KiB extension as we'll + * be using 4KiB pages for uniquification. + */ +static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize) +{ + int start = num_wired_entries(); + unsigned long long vpn_mask; + bool global; + int i; - htw_stop(); + vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT); + vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31; - for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { - unsigned long vpn; + for (i = 0; i < tlbsize; i++) { + unsigned long long entryhi, vpn, mask, asid; + unsigned int pagesz; write_c0_index(i); mtc0_tlbr_hazard(); tlb_read(); tlb_read_hazard(); - vpn = read_c0_entryhi(); - vpn &= vpn_mask & PAGE_MASK; - tlb_vpns[cnt] = vpn; - /* Prevent any large pages from overlapping regular ones. */ - write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); - mtc0_tlbw_hazard(); - tlb_write_indexed(); - tlbw_use_hazard(); + global = !!(read_c0_entrylo0() & ENTRYLO_G); + entryhi = read_c0_entryhi_native(); + mask = read_c0_pagemask(); + + asid = entryhi & cpu_asid_mask(¤t_cpu_data); + vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT; + pagesz = ilog2((mask >> VPN2_SHIFT) + 1); + + tlb_vpns[i].global = global; + tlb_vpns[i].asid = global ? 0 : asid; + tlb_vpns[i].vpn = vpn; + tlb_vpns[i].pagesz = pagesz; + tlb_vpns[i].wired = i < start; + tlb_vpns[i].index = i; } +} - sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); +/* + * Write unique values to all but the wired TLB entries each, using + * the 4KiB page size. This size might not be supported with R6, but + * EHINV is mandatory for R6, so we won't ever be called in that case. + * + * A sorted table is supplied with any wired entries at the beginning, + * followed by any global entries, and then finally regular entries. + * We start at the VPN and ASID values of zero and only assign user + * addresses, therefore guaranteeing no clash with addresses produced + * by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global + * entries, by increasing the VPN value beyond the span of such entry. + * + * When a VPN/ASID clash is found with a regular entry we increment the + * ASID instead until no VPN/ASID clash has been found or the ASID space + * has been exhausted, in which case we increase the VPN value beyond + * the span of the largest clashing entry. + * + * We do not need to be concerned about FTLB or MMID configurations as + * those are required to implement the EHINV feature. + */ +static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize) +{ + unsigned long long asid, vpn, vpn_size, pagesz; + int widx, gidx, idx, sidx, lidx, i; - write_c0_pagemask(PM_DEFAULT_MASK); + vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT); + pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1); + + write_c0_pagemask(PM_4K); write_c0_entrylo0(0); write_c0_entrylo1(0); - idx = 0; - ent = tlbsize; - for (i = start; i < tlbsize; i++) - while (1) { - unsigned long entryhi, vpn; + asid = 0; + vpn = 0; + widx = 0; + gidx = 0; + for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++) + ; + for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++) + ; + idx = gidx = sidx + 1; + for (i = sidx; i < tlbsize; i++) { + unsigned long long entryhi, vpn_pagesz = 0; - entryhi = UNIQUE_ENTRYHI(ent); - vpn = entryhi & vpn_mask & PAGE_MASK; + while (1) { + if (WARN_ON(vpn >= vpn_size)) { + dump_tlb_all(); + /* Pray local_flush_tlb_all() will cope. */ + return; + } - if (idx >= cnt || vpn < tlb_vpns[idx]) { - write_c0_entryhi(entryhi); - write_c0_index(i); - mtc0_tlbw_hazard(); - tlb_write_indexed(); - ent++; - break; - } else if (vpn == tlb_vpns[idx]) { - ent++; - } else { + /* VPN must be below the next wired entry. */ + if (widx < sidx && vpn >= tlb_vpns[widx].vpn) { + vpn = max(vpn, + (tlb_vpns[widx].vpn + + (1ULL << tlb_vpns[widx].pagesz))); + asid = 0; + widx++; + continue; + } + /* VPN must be below the next global entry. */ + if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) { + vpn = max(vpn, + (tlb_vpns[gidx].vpn + + (1ULL << tlb_vpns[gidx].pagesz))); + asid = 0; + gidx++; + continue; + } + /* Try to find a free ASID so as to conserve VPNs. */ + if (idx < tlbsize && vpn == tlb_vpns[idx].vpn && + asid == tlb_vpns[idx].asid) { + unsigned long long idx_pagesz; + + idx_pagesz = tlb_vpns[idx].pagesz; + vpn_pagesz = max(vpn_pagesz, idx_pagesz); + do + idx++; + while (idx < tlbsize && + vpn == tlb_vpns[idx].vpn && + asid == tlb_vpns[idx].asid); + asid++; + if (asid > cpu_asid_mask(¤t_cpu_data)) { + vpn += vpn_pagesz; + asid = 0; + vpn_pagesz = 0; + } + continue; + } + /* VPN mustn't be above the next regular entry. */ + if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) { + vpn = max(vpn, + (tlb_vpns[idx].vpn + + (1ULL << tlb_vpns[idx].pagesz))); + asid = 0; idx++; + continue; } + break; } + entryhi = (vpn << VPN2_SHIFT) | asid; + write_c0_entryhi_native(entryhi); + write_c0_index(tlb_vpns[i].index); + mtc0_tlbw_hazard(); + tlb_write_indexed(); + + tlb_vpns[i].asid = asid; + tlb_vpns[i].vpn = vpn; + tlb_vpns[i].pagesz = pagesz; + + asid++; + if (asid > cpu_asid_mask(¤t_cpu_data)) { + vpn += 1ULL << pagesz; + asid = 0; + } + } +} + +/* + * Initialise all TLB entries with unique values that do not clash with + * what we have been handed over and what we'll be using ourselves. + */ +static void __ref r4k_tlb_uniquify(void) +{ + int tlbsize = current_cpu_data.tlbsize; + bool use_slab = slab_is_available(); + phys_addr_t tlb_vpn_size; + struct tlbent *tlb_vpns; + + tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); + tlb_vpns = (use_slab ? + kmalloc(tlb_vpn_size, GFP_KERNEL) : + memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns))); + if (WARN_ON(!tlb_vpns)) + return; /* Pray local_flush_tlb_all() is good enough. */ + + htw_stop(); + + r4k_tlb_uniquify_read(tlb_vpns, tlbsize); + + sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL); + + r4k_tlb_uniquify_write(tlb_vpns, tlbsize); + + write_c0_pagemask(PM_DEFAULT_MASK); + tlbw_use_hazard(); htw_start(); flush_micro_tlb(); From 703ccb63ae9f7444d6ff876d024e17f628103c69 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 31 Mar 2026 18:07:39 -0700 Subject: [PATCH 320/700] workqueue: Add pool_workqueue to pending_pwqs list when unplugging multiple inactive works MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In unplug_oldest_pwq(), the first inactive work item on the pool_workqueue is activated correctly. However, if multiple inactive works exist on the same pool_workqueue, subsequent works fail to activate because wq_node_nr_active.pending_pwqs is empty — the list insertion is skipped when the pool_workqueue is plugged. Fix this by checking for additional inactive works in unplug_oldest_pwq() and updating wq_node_nr_active.pending_pwqs accordingly. Fixes: 4c065dbce1e8 ("workqueue: Enable unbound cpumask update on ordered workqueues") Cc: stable@vger.kernel.org Cc: Carlos Santa Cc: Ryan Neph Cc: Lai Jiangshan Cc: Waiman Long Cc: linux-kernel@vger.kernel.org Signed-off-by: Matthew Brost Signed-off-by: Tejun Heo Acked-by: Waiman Long --- kernel/workqueue.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index eda756556341ab..c6ea96d5b71672 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1849,8 +1849,20 @@ static void unplug_oldest_pwq(struct workqueue_struct *wq) raw_spin_lock_irq(&pwq->pool->lock); if (pwq->plugged) { pwq->plugged = false; - if (pwq_activate_first_inactive(pwq, true)) + if (pwq_activate_first_inactive(pwq, true)) { + /* + * While plugged, queueing skips activation which + * includes bumping the nr_active count and adding the + * pwq to nna->pending_pwqs if the count can't be + * obtained. We need to restore both for the pwq being + * unplugged. The first call activates the first + * inactive work item and the second, if there are more + * inactive, puts the pwq on pending_pwqs. + */ + pwq_activate_first_inactive(pwq, false); + kick_pool(pwq->pool); + } } raw_spin_unlock_irq(&pwq->pool->lock); } From 01cc50ea5167bb14117257ec084637abe9e5f691 Mon Sep 17 00:00:00 2001 From: Stefan Wiehler Date: Tue, 10 Mar 2026 11:40:24 +0100 Subject: [PATCH 321/700] mips: mm: Allocate tlb_vpn array atomically Found by DEBUG_ATOMIC_SLEEP: BUG: sleeping function called from invalid context at /include/linux/sched/mm.h:306 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/1 preempt_count: 1, expected: 0 RCU nest depth: 0, expected: 0 no locks held by swapper/1/0. irq event stamp: 0 hardirqs last enabled at (0): [<0000000000000000>] 0x0 hardirqs last disabled at (0): [] copy_process+0x75c/0x1b68 softirqs last enabled at (0): [] copy_process+0x75c/0x1b68 softirqs last disabled at (0): [<0000000000000000>] 0x0 CPU: 1 PID: 0 Comm: swapper/1 Not tainted 6.6.119-d79e757675ec-fct #1 Stack : 800000000290bad8 0000000000000000 0000000000000008 800000000290bae8 800000000290bae8 800000000290bc78 0000000000000000 0000000000000000 ffffffff80c80000 0000000000000001 ffffffff80d8dee8 ffffffff810d09c0 784bb2a7ec10647d 0000000000000010 ffffffff80a6fd60 8000000001d8a9c0 0000000000000000 0000000000000000 ffffffff80d90000 0000000000000000 ffffffff80c9e0e8 0000000007ffffff 0000000000000cc0 0000000000000400 ffffffffffffffff 0000000000000001 0000000000000002 ffffffffc0149ed8 fffffffffffffffe 8000000002908000 800000000290bae0 ffffffff80a81b74 ffffffff80129fb0 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 ffffffff80129fd0 0000000000000000 ... Call Trace: [] show_stack+0x60/0x158 [] dump_stack_lvl+0x88/0xbc [] __might_resched+0x268/0x288 [] __kmem_cache_alloc_node+0x2e0/0x330 [] __kmalloc+0x58/0xd0 [] r4k_tlb_uniquify+0x7c/0x428 [] tlb_init+0x7c/0x110 [] per_cpu_trap_init+0x16c/0x1d0 [] start_secondary+0x28/0x128 Fixes: 231ac951faba ("MIPS: mm: kmalloc tlb_vpn array to avoid stack overflow") Signed-off-by: Stefan Wiehler Cc: stable@vger.kernel.org Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/tlb-r4k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index 08c3e65bf0f29d..24fe85fa169d16 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -751,7 +751,7 @@ static void __ref r4k_tlb_uniquify(void) tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? - kmalloc(tlb_vpn_size, GFP_KERNEL) : + kmalloc(tlb_vpn_size, GFP_ATOMIC) : memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns))); if (WARN_ON(!tlb_vpns)) return; /* Pray local_flush_tlb_all() is good enough. */ From a834a0b66ec6fb743377201a0f4229bb2503f4ce Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:46 +0200 Subject: [PATCH 322/700] Bluetooth: hci_sync: call destroy in hci_cmd_sync_run if immediate hci_cmd_sync_run() may run the work immediately if called from existing sync work (otherwise it queues a new sync work). In this case it fails to call the destroy() function. On immediate run, make it behave same way as if item was queued successfully: call destroy, and return 0. The only callsite is hci_abort_conn() via hci_cmd_sync_run_once(), and this changes its return value. However, its return value is not used except as the return value for hci_disconnect(), and nothing uses the return value of hci_disconnect(). Hence there should be no behavior change anywhere. Fixes: c898f6d7b093b ("Bluetooth: hci_sync: Introduce hci_cmd_sync_run/hci_cmd_sync_run_once") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 45d16639874a0b..6283a4df78b005 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -801,8 +801,15 @@ int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, return -ENETDOWN; /* If on cmd_sync_work then run immediately otherwise queue */ - if (current_work() == &hdev->cmd_sync_work) - return func(hdev, data); + if (current_work() == &hdev->cmd_sync_work) { + int err; + + err = func(hdev, data); + if (destroy) + destroy(hdev, data, err); + + return 0; + } return hci_cmd_sync_submit(hdev, func, data, destroy); } From 8a5b0135d4a5d9683203a3d9a12a711ccec5936b Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 26 Mar 2026 23:16:45 +0800 Subject: [PATCH 323/700] Bluetooth: SCO: fix race conditions in sco_sock_connect() sco_sock_connect() checks sk_state and sk_type without holding the socket lock. Two concurrent connect() syscalls on the same socket can both pass the check and enter sco_connect(), leading to use-after-free. The buggy scenario involves three participants and was confirmed with additional logging instrumentation: Thread A (connect): HCI disconnect: Thread B (connect): sco_sock_connect(sk) sco_sock_connect(sk) sk_state==BT_OPEN sk_state==BT_OPEN (pass, no lock) (pass, no lock) sco_connect(sk): sco_connect(sk): hci_dev_lock hci_dev_lock hci_connect_sco <- blocked -> hcon1 sco_conn_add->conn1 lock_sock(sk) sco_chan_add: conn1->sk = sk sk->conn = conn1 sk_state=BT_CONNECT release_sock hci_dev_unlock hci_dev_lock sco_conn_del: lock_sock(sk) sco_chan_del: sk->conn=NULL conn1->sk=NULL sk_state= BT_CLOSED SOCK_ZAPPED release_sock hci_dev_unlock (unblocked) hci_connect_sco -> hcon2 sco_conn_add -> conn2 lock_sock(sk) sco_chan_add: sk->conn=conn2 sk_state= BT_CONNECT // zombie sk! release_sock hci_dev_unlock Thread B revives a BT_CLOSED + SOCK_ZAPPED socket back to BT_CONNECT. Subsequent cleanup triggers double sock_put() and use-after-free. Meanwhile conn1 is leaked as it was orphaned when sco_conn_del() cleared the association. Fix this by: - Moving lock_sock() before the sk_state/sk_type checks in sco_sock_connect() to serialize concurrent connect attempts - Fixing the sk_type != SOCK_SEQPACKET check to actually return the error instead of just assigning it - Adding a state re-check in sco_connect() after lock_sock() to catch state changes during the window between the locks - Adding sco_pi(sk)->conn check in sco_chan_add() to prevent double-attach of a socket to multiple connections - Adding hci_conn_drop() on sco_chan_add failure to prevent HCI connection leaks Fixes: 9a8ec9e8ebb5 ("Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm") Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 584e059de20a66..b84587811ef4f8 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -298,7 +298,7 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk, int err = 0; sco_conn_lock(conn); - if (conn->sk) + if (conn->sk || sco_pi(sk)->conn) err = -EBUSY; else __sco_chan_add(conn, sk, parent); @@ -353,9 +353,20 @@ static int sco_connect(struct sock *sk) lock_sock(sk); + /* Recheck state after reacquiring the socket lock, as another + * thread may have changed it (e.g., closed the socket). + */ + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + release_sock(sk); + hci_conn_drop(hcon); + err = -EBADFD; + goto unlock; + } + err = sco_chan_add(conn, sk, NULL); if (err) { release_sock(sk); + hci_conn_drop(hcon); goto unlock; } @@ -656,13 +667,18 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, addr->sa_family != AF_BLUETOOTH) return -EINVAL; - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) + lock_sock(sk); + + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { + release_sock(sk); return -EBADFD; + } - if (sk->sk_type != SOCK_SEQPACKET) - err = -EINVAL; + if (sk->sk_type != SOCK_SEQPACKET) { + release_sock(sk); + return -EINVAL; + } - lock_sock(sk); /* Set destination address and psm */ bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr); release_sock(sk); From 2b2bf47cd75518c36fa2d41380e4a40641cc89cd Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Thu, 26 Mar 2026 17:31:24 +0000 Subject: [PATCH 324/700] Bluetooth: hci_event: move wake reason storage into validated event handlers hci_store_wake_reason() is called from hci_event_packet() immediately after stripping the HCI event header but before hci_event_func() enforces the per-event minimum payload length from hci_ev_table. This means a short HCI event frame can reach bacpy() before any bounds check runs. Rather than duplicating skb parsing and per-event length checks inside hci_store_wake_reason(), move wake-address storage into the individual event handlers after their existing event-length validation has succeeded. Convert hci_store_wake_reason() into a small helper that only stores an already-validated bdaddr while the caller holds hci_dev_lock(). Use the same helper after hci_event_func() with a NULL address to preserve the existing unexpected-wake fallback semantics when no validated event handler records a wake address. Annotate the helper with __must_hold(&hdev->lock) and add lockdep_assert_held(&hdev->lock) so future call paths keep the lock contract explicit. Call the helper from hci_conn_request_evt(), hci_conn_complete_evt(), hci_sync_conn_complete_evt(), le_conn_complete_evt(), hci_le_adv_report_evt(), hci_le_ext_adv_report_evt(), hci_le_direct_adv_report_evt(), hci_le_pa_sync_established_evt(), and hci_le_past_received_evt(). Fixes: 2f20216c1d6f ("Bluetooth: Emit controller suspend and resume events") Cc: stable@vger.kernel.org Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 94 +++++++++++++++------------------------ 1 file changed, 35 insertions(+), 59 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 286529d2e554a4..81d2f9a3eec967 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -80,6 +80,10 @@ static void *hci_le_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb, return data; } +static void hci_store_wake_reason(struct hci_dev *hdev, + const bdaddr_t *bdaddr, u8 addr_type) + __must_hold(&hdev->lock); + static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -3111,6 +3115,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); /* Check for existing connection: * @@ -3274,6 +3279,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "bdaddr %pMR type 0x%x", &ev->bdaddr, ev->link_type); + hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); + hci_dev_unlock(hdev); + /* Reject incoming connection from device with same BD ADDR against * CVE-2020-26555 */ @@ -5021,6 +5030,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { @@ -5713,6 +5723,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, int err; hci_dev_lock(hdev); + hci_store_wake_reason(hdev, bdaddr, bdaddr_type); /* All controllers implicitly stop advertising in the event of a * connection, so ensure that the state bit is cleared. @@ -6005,6 +6016,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type); hci_dev_clear_flag(hdev, HCI_PA_SYNC); @@ -6403,6 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data, info->length + 1)) break; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + if (info->length <= max_adv_len(hdev)) { rssi = info->data[info->length]; process_adv_report(hdev, info->type, &info->bdaddr, @@ -6491,6 +6505,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, info->length)) break; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK; legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type); @@ -6536,6 +6552,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data, bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); hci_dev_lock(hdev); + hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type); hci_dev_clear_flag(hdev, HCI_PA_SYNC); @@ -6834,6 +6851,8 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data, for (i = 0; i < ev->num; i++) { struct hci_ev_le_direct_adv_info *info = &ev->info[i]; + hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type); + process_adv_report(hdev, info->type, &info->bdaddr, info->bdaddr_type, &info->direct_addr, info->direct_addr_type, HCI_ADV_PHY_1M, 0, @@ -7517,73 +7536,29 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, return true; } -static void hci_store_wake_reason(struct hci_dev *hdev, u8 event, - struct sk_buff *skb) +static void hci_store_wake_reason(struct hci_dev *hdev, + const bdaddr_t *bdaddr, u8 addr_type) + __must_hold(&hdev->lock) { - struct hci_ev_le_advertising_info *adv; - struct hci_ev_le_direct_adv_info *direct_adv; - struct hci_ev_le_ext_adv_info *ext_adv; - const struct hci_ev_conn_complete *conn_complete = (void *)skb->data; - const struct hci_ev_conn_request *conn_request = (void *)skb->data; - - hci_dev_lock(hdev); + lockdep_assert_held(&hdev->lock); /* If we are currently suspended and this is the first BT event seen, * save the wake reason associated with the event. */ if (!hdev->suspended || hdev->wake_reason) - goto unlock; + return; + + if (!bdaddr) { + hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED; + return; + } /* Default to remote wake. Values for wake_reason are documented in the * Bluez mgmt api docs. */ hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE; - - /* Once configured for remote wakeup, we should only wake up for - * reconnections. It's useful to see which device is waking us up so - * keep track of the bdaddr of the connection event that woke us up. - */ - if (event == HCI_EV_CONN_REQUEST) { - bacpy(&hdev->wake_addr, &conn_request->bdaddr); - hdev->wake_addr_type = BDADDR_BREDR; - } else if (event == HCI_EV_CONN_COMPLETE) { - bacpy(&hdev->wake_addr, &conn_complete->bdaddr); - hdev->wake_addr_type = BDADDR_BREDR; - } else if (event == HCI_EV_LE_META) { - struct hci_ev_le_meta *le_ev = (void *)skb->data; - u8 subevent = le_ev->subevent; - u8 *ptr = &skb->data[sizeof(*le_ev)]; - u8 num_reports = *ptr; - - if ((subevent == HCI_EV_LE_ADVERTISING_REPORT || - subevent == HCI_EV_LE_DIRECT_ADV_REPORT || - subevent == HCI_EV_LE_EXT_ADV_REPORT) && - num_reports) { - adv = (void *)(ptr + 1); - direct_adv = (void *)(ptr + 1); - ext_adv = (void *)(ptr + 1); - - switch (subevent) { - case HCI_EV_LE_ADVERTISING_REPORT: - bacpy(&hdev->wake_addr, &adv->bdaddr); - hdev->wake_addr_type = adv->bdaddr_type; - break; - case HCI_EV_LE_DIRECT_ADV_REPORT: - bacpy(&hdev->wake_addr, &direct_adv->bdaddr); - hdev->wake_addr_type = direct_adv->bdaddr_type; - break; - case HCI_EV_LE_EXT_ADV_REPORT: - bacpy(&hdev->wake_addr, &ext_adv->bdaddr); - hdev->wake_addr_type = ext_adv->bdaddr_type; - break; - } - } - } else { - hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED; - } - -unlock: - hci_dev_unlock(hdev); + bacpy(&hdev->wake_addr, bdaddr); + hdev->wake_addr_type = addr_type; } #define HCI_EV_VL(_op, _func, _min_len, _max_len) \ @@ -7830,14 +7805,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, HCI_EVENT_HDR_SIZE); - /* Store wake reason if we're suspended */ - hci_store_wake_reason(hdev, event, skb); - bt_dev_dbg(hdev, "event 0x%2.2x", event); hci_event_func(hdev, event, skb, &opcode, &status, &req_complete, &req_complete_skb); + hci_dev_lock(hdev); + hci_store_wake_reason(hdev, NULL, 0); + hci_dev_unlock(hdev); + if (req_complete) { req_complete(hdev, status, opcode); } else if (req_complete_skb) { From 2969554bcfccb5c609f6b6cd4a014933f3a66dd0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:43 +0200 Subject: [PATCH 325/700] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists hci_cmd_sync_queue_once() needs to indicate whether a queue item was added, so caller can know if callbacks are called, so it can avoid leaking resources. Change the function to return -EEXIST if queue item already exists. Modify all callsites to handle that. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 53 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 6283a4df78b005..97745710e3ce70 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy) { if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) - return 0; + return -EEXIST; return hci_cmd_sync_queue(hdev, func, data, destroy); } @@ -3262,6 +3262,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data) int hci_update_passive_scan(struct hci_dev *hdev) { + int err; + /* Only queue if it would have any effect */ if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || @@ -3271,8 +3273,9 @@ int hci_update_passive_scan(struct hci_dev *hdev) hci_dev_test_flag(hdev, HCI_UNREGISTER)) return 0; - return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL, - NULL); + err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL, + NULL); + return (err == -EEXIST) ? 0 : err; } int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val) @@ -6965,8 +6968,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, - NULL); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, + NULL); + return (err == -EEXIST) ? 0 : err; } static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) @@ -7002,8 +7008,11 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, - create_le_conn_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + return (err == -EEXIST) ? 0 : err; } int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -7210,8 +7219,11 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data) int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, - create_pa_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, + create_pa_complete); + return (err == -EEXIST) ? 0 : err; } static void create_big_complete(struct hci_dev *hdev, void *data, int err) @@ -7273,8 +7285,11 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn) { - return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, - create_big_complete); + int err; + + err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, + create_big_complete); + return (err == -EEXIST) ? 0 : err; } struct past_data { @@ -7366,7 +7381,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) if (err) kfree(data); - return err; + return (err == -EEXIST) ? 0 : err; } static void le_read_features_complete(struct hci_dev *hdev, void *data, int err) @@ -7453,7 +7468,7 @@ int hci_le_read_remote_features(struct hci_conn *conn) else err = -EOPNOTSUPP; - return err; + return (err == -EEXIST) ? 0 : err; } static void pkt_type_changed(struct hci_dev *hdev, void *data, int err) @@ -7479,6 +7494,7 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) { struct hci_dev *hdev = conn->hdev; struct hci_cp_change_conn_ptype *cp; + int err; cp = kmalloc_obj(*cp); if (!cp) @@ -7487,8 +7503,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) cp->handle = cpu_to_le16(conn->handle); cp->pkt_type = cpu_to_le16(pkt_type); - return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, - pkt_type_changed); + err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, + pkt_type_changed); + return (err == -EEXIST) ? 0 : err; } static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err) @@ -7514,6 +7531,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) { struct hci_dev *hdev = conn->hdev; struct hci_cp_le_set_phy *cp; + int err; cp = kmalloc_obj(*cp); if (!cp) @@ -7524,6 +7542,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) cp->tx_phys = tx_phys; cp->rx_phys = rx_phys; - return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, - le_phy_update_complete); + err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, + le_phy_update_complete); + return (err == -EEXIST) ? 0 : err; } From aca377208e7f7322bf4e107cdec6e7d7e8aa7a88 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:44 +0200 Subject: [PATCH 326/700] Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once fails When hci_cmd_sync_queue_once() returns with error, the destroy callback will not be called. Fix leaking references / memory on these failures. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 97745710e3ce70..8cbbba50e77e1e 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7460,13 +7460,16 @@ int hci_le_read_remote_features(struct hci_conn *conn) * role is possible. Otherwise just transition into the * connected state without requesting the remote features. */ - if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) + if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) { err = hci_cmd_sync_queue_once(hdev, hci_le_read_remote_features_sync, hci_conn_hold(conn), le_read_features_complete); - else + if (err) + hci_conn_drop(conn); + } else { err = -EOPNOTSUPP; + } return (err == -EEXIST) ? 0 : err; } @@ -7505,6 +7508,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, pkt_type_changed); + if (err) + kfree(cp); + return (err == -EEXIST) ? 0 : err; } @@ -7544,5 +7550,8 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, le_phy_update_complete); + if (err) + kfree(cp); + return (err == -EEXIST) ? 0 : err; } From 035c25007c9e698bef3826070ee34bb6d778020c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 25 Mar 2026 11:11:46 -0400 Subject: [PATCH 327/700] Bluetooth: hci_sync: Fix UAF in le_read_features_complete This fixes the following backtrace caused by hci_conn being freed before le_read_features_complete but after hci_le_read_remote_features_sync so hci_conn_del -> hci_cmd_sync_dequeue is not able to prevent it: ================================================================== BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:96 [inline] BUG: KASAN: slab-use-after-free in atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline] BUG: KASAN: slab-use-after-free in hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline] BUG: KASAN: slab-use-after-free in le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344 Write of size 4 at addr ffff8880796b0010 by task kworker/u9:0/52 CPU: 0 UID: 0 PID: 52 Comm: kworker/u9:0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 Workqueue: hci0 hci_cmd_sync_work Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xcd/0x630 mm/kasan/report.c:482 kasan_report+0xe0/0x110 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:194 [inline] kasan_check_range+0x100/0x1b0 mm/kasan/generic.c:200 instrument_atomic_read_write include/linux/instrumented.h:96 [inline] atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline] hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline] le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344 hci_cmd_sync_work+0x1ff/0x430 net/bluetooth/hci_sync.c:334 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 Allocated by task 5932: kasan_save_stack+0x33/0x60 mm/kasan/common.c:56 kasan_save_track+0x14/0x30 mm/kasan/common.c:77 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:417 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] __hci_conn_add+0xf8/0x1c70 net/bluetooth/hci_conn.c:963 hci_conn_add_unset+0x76/0x100 net/bluetooth/hci_conn.c:1084 le_conn_complete_evt+0x639/0x1f20 net/bluetooth/hci_event.c:5714 hci_le_enh_conn_complete_evt+0x23d/0x380 net/bluetooth/hci_event.c:5861 hci_le_meta_evt+0x357/0x5e0 net/bluetooth/hci_event.c:7408 hci_event_func net/bluetooth/hci_event.c:7716 [inline] hci_event_packet+0x685/0x11c0 net/bluetooth/hci_event.c:7773 hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 Freed by task 5932: kasan_save_stack+0x33/0x60 mm/kasan/common.c:56 kasan_save_track+0x14/0x30 mm/kasan/common.c:77 __kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:587 kasan_save_free_info mm/kasan/kasan.h:406 [inline] poison_slab_object mm/kasan/common.c:252 [inline] __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:284 kasan_slab_free include/linux/kasan.h:234 [inline] slab_free_hook mm/slub.c:2540 [inline] slab_free mm/slub.c:6663 [inline] kfree+0x2f8/0x6e0 mm/slub.c:6871 device_release+0xa4/0x240 drivers/base/core.c:2565 kobject_cleanup lib/kobject.c:689 [inline] kobject_release lib/kobject.c:720 [inline] kref_put include/linux/kref.h:65 [inline] kobject_put+0x1e7/0x590 lib/kobject.c:737 put_device drivers/base/core.c:3797 [inline] device_unregister+0x2f/0xc0 drivers/base/core.c:3920 hci_conn_del_sysfs+0xb4/0x180 net/bluetooth/hci_sysfs.c:79 hci_conn_cleanup net/bluetooth/hci_conn.c:173 [inline] hci_conn_del+0x657/0x1180 net/bluetooth/hci_conn.c:1234 hci_disconn_complete_evt+0x410/0xa00 net/bluetooth/hci_event.c:3451 hci_event_func net/bluetooth/hci_event.c:7719 [inline] hci_event_packet+0xa10/0x11c0 net/bluetooth/hci_event.c:7773 hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421 kthread+0x3c5/0x780 kernel/kthread.c:463 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 The buggy address belongs to the object at ffff8880796b0000 which belongs to the cache kmalloc-8k of size 8192 The buggy address is located 16 bytes inside of freed 8192-byte region [ffff8880796b0000, ffff8880796b2000) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x796b0 head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 anon flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff) page_type: f5(slab) raw: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001 raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001 head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 00fff00000000003 ffffea0001e5ac01 00000000ffffffff 00000000ffffffff head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2040(__GFP_IO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5657, tgid 5657 (dhcpcd-run-hook), ts 79819636908, free_ts 79814310558 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0x1af/0x220 mm/page_alloc.c:1845 prep_new_page mm/page_alloc.c:1853 [inline] get_page_from_freelist+0xd0b/0x31a0 mm/page_alloc.c:3879 __alloc_frozen_pages_noprof+0x25f/0x2440 mm/page_alloc.c:5183 alloc_pages_mpol+0x1fb/0x550 mm/mempolicy.c:2416 alloc_slab_page mm/slub.c:3075 [inline] allocate_slab mm/slub.c:3248 [inline] new_slab+0x2c3/0x430 mm/slub.c:3302 ___slab_alloc+0xe18/0x1c90 mm/slub.c:4651 __slab_alloc.constprop.0+0x63/0x110 mm/slub.c:4774 __slab_alloc_node mm/slub.c:4850 [inline] slab_alloc_node mm/slub.c:5246 [inline] __kmalloc_cache_noprof+0x477/0x800 mm/slub.c:5766 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] tomoyo_print_bprm security/tomoyo/audit.c:26 [inline] tomoyo_init_log+0xc8a/0x2140 security/tomoyo/audit.c:264 tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198 tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline] tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63 tomoyo_environ security/tomoyo/domain.c:672 [inline] tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888 tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline] tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92 security_bprm_check+0x1b9/0x1e0 security/security.c:794 search_binary_handler fs/exec.c:1659 [inline] exec_binprm fs/exec.c:1701 [inline] bprm_execve fs/exec.c:1753 [inline] bprm_execve+0x81e/0x1620 fs/exec.c:1729 do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859 page last free pid 5657 tgid 5657 stack trace: reset_page_owner include/linux/page_owner.h:25 [inline] free_pages_prepare mm/page_alloc.c:1394 [inline] __free_frozen_pages+0x7df/0x1160 mm/page_alloc.c:2901 discard_slab mm/slub.c:3346 [inline] __put_partials+0x130/0x170 mm/slub.c:3886 qlink_free mm/kasan/quarantine.c:163 [inline] qlist_free_all+0x4c/0xf0 mm/kasan/quarantine.c:179 kasan_quarantine_reduce+0x195/0x1e0 mm/kasan/quarantine.c:286 __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:352 kasan_slab_alloc include/linux/kasan.h:252 [inline] slab_post_alloc_hook mm/slub.c:4948 [inline] slab_alloc_node mm/slub.c:5258 [inline] __kmalloc_cache_noprof+0x274/0x800 mm/slub.c:5766 kmalloc_noprof include/linux/slab.h:957 [inline] tomoyo_print_header security/tomoyo/audit.c:156 [inline] tomoyo_init_log+0x197/0x2140 security/tomoyo/audit.c:255 tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198 tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline] tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63 tomoyo_environ security/tomoyo/domain.c:672 [inline] tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888 tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline] tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92 security_bprm_check+0x1b9/0x1e0 security/security.c:794 search_binary_handler fs/exec.c:1659 [inline] exec_binprm fs/exec.c:1701 [inline] bprm_execve fs/exec.c:1753 [inline] bprm_execve+0x81e/0x1620 fs/exec.c:1729 do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859 do_execve fs/exec.c:1933 [inline] __do_sys_execve fs/exec.c:2009 [inline] __se_sys_execve fs/exec.c:2004 [inline] __x64_sys_execve+0x8e/0xb0 fs/exec.c:2004 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0xf80 arch/x86/entry/syscall_64.c:94 Memory state around the buggy address: ffff8880796aff00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8880796aff80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff8880796b0000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8880796b0080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8880796b0100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: a106e50be74b ("Bluetooth: HCI: Add support for LL Extended Feature Set") Reported-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com Tested-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=87badbb9094e008e0685 Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Pauli Virtanen --- net/bluetooth/hci_sync.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 8cbbba50e77e1e..ffb0ceda6f7b58 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7390,10 +7390,8 @@ static void le_read_features_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); - if (err == -ECANCELED) - return; - hci_conn_drop(conn); + hci_conn_put(conn); } static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev, @@ -7463,10 +7461,12 @@ int hci_le_read_remote_features(struct hci_conn *conn) if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) { err = hci_cmd_sync_queue_once(hdev, hci_le_read_remote_features_sync, - hci_conn_hold(conn), + hci_conn_hold(hci_conn_get(conn)), le_read_features_complete); - if (err) + if (err) { hci_conn_drop(conn); + hci_conn_put(conn); + } } else { err = -EOPNOTSUPP; } From 0ffac654e95c1bdfe2d4edf28fb18d6ba1f103e6 Mon Sep 17 00:00:00 2001 From: Jonathan Rissanen Date: Fri, 27 Mar 2026 11:47:21 +0100 Subject: [PATCH 328/700] Bluetooth: hci_h4: Fix race during initialization Commit 5df5dafc171b ("Bluetooth: hci_uart: Fix another race during initialization") fixed a race for hci commands sent during initialization. However, there is still a race that happens if an hci event from one of these commands is received before HCI_UART_REGISTERED has been set at the end of hci_uart_register_dev(). The event will be ignored which causes the command to fail with a timeout in the log: "Bluetooth: hci0: command 0x1003 tx timeout" This is because the hci event receive path (hci_uart_tty_receive -> h4_recv) requires HCI_UART_REGISTERED to be set in h4_recv(), while the hci command transmit path (hci_uart_send_frame -> h4_enqueue) only requires HCI_UART_PROTO_INIT to be set in hci_uart_send_frame(). The check for HCI_UART_REGISTERED was originally added in commit c2578202919a ("Bluetooth: Fix H4 crash from incoming UART packets") to fix a crash caused by hu->hdev being null dereferenced. That can no longer happen: once HCI_UART_PROTO_INIT is set in hci_uart_register_dev() all pointers (hu, hu->priv and hu->hdev) are valid, and hci_uart_tty_receive() already calls h4_recv() on HCI_UART_PROTO_INIT or HCI_UART_PROTO_READY. Remove the check for HCI_UART_REGISTERED in h4_recv() to fix the race condition. Fixes: 5df5dafc171b ("Bluetooth: hci_uart: Fix another race during initialization") Signed-off-by: Jonathan Rissanen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_h4.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 07cc2a79a77bf8..a889a66a326f7a 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -109,9 +109,6 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count) { struct h4_struct *h4 = hu->priv; - if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) - return -EUNATCH; - h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count, h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); if (IS_ERR(h4->rx_skb)) { From b8dbe9648d69059cfe3a28917bfbf7e61efd7f15 Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Sat, 28 Mar 2026 16:46:47 +0800 Subject: [PATCH 329/700] Bluetooth: MGMT: validate LTK enc_size on load Load Long Term Keys stores the user-provided enc_size and later uses it to size fixed-size stack operations when replying to LE LTK requests. An enc_size larger than the 16-byte key buffer can therefore overflow the reply stack buffer. Reject oversized enc_size values while validating the management LTK record so invalid keys never reach the stored key state. Fixes: 346af67b8d11 ("Bluetooth: Add MGMT handlers for dealing with SMP LTK's") Reported-by: Keenan Dong Signed-off-by: Keenan Dong Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5f9287fb826ac..adcd86c15b4e63 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7248,6 +7248,9 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key) if (key->initiator != 0x00 && key->initiator != 0x01) return false; + if (key->enc_size > sizeof(key->val)) + return false; + switch (key->addr.type) { case BDADDR_LE_PUBLIC: return true; From a2639a7f0f5bf7d73f337f8f077c19415c62ed2c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 29 Mar 2026 16:43:01 +0300 Subject: [PATCH 330/700] Bluetooth: hci_conn: fix potential UAF in set_cig_params_sync hci_conn lookup and field access must be covered by hdev lock in set_cig_params_sync, otherwise it's possible it is freed concurrently. Take hdev lock to prevent hci_conn from being deleted or modified concurrently. Just RCU lock is not suitable here, as we also want to avoid "tearing" in the configuration. Fixes: a091289218202 ("Bluetooth: hci_conn: Fix hci_le_set_cig_params") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e6393f17576b0b..11d3ad8d255145 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1843,9 +1843,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) u8 aux_num_cis = 0; u8 cis_id; + hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_cig(hdev, cig_id); - if (!conn) + if (!conn) { + hci_dev_unlock(hdev); return 0; + } qos = &conn->iso_qos; pdu->cig_id = cig_id; @@ -1884,6 +1888,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) } pdu->num_cis = aux_num_cis; + hci_dev_unlock(hdev); + if (!pdu->num_cis) return 0; From b255531b27da336571411248c2a72a350662bd09 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 29 Mar 2026 16:43:02 +0300 Subject: [PATCH 331/700] Bluetooth: hci_event: fix potential UAF in hci_le_remote_conn_param_req_evt hci_conn lookup and field access must be covered by hdev lock in hci_le_remote_conn_param_req_evt, otherwise it's possible it is freed concurrently. Extend the hci_dev_lock critical section to cover all conn usage. Fixes: 95118dd4edfec ("Bluetooth: hci_event: Use of a function table to handle LE subevents") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_event.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 81d2f9a3eec967..3ebc5e6d45d98a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -6784,25 +6784,31 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, latency = le16_to_cpu(ev->latency); timeout = le16_to_cpu(ev->timeout); + hci_dev_lock(hdev); + hcon = hci_conn_hash_lookup_handle(hdev, handle); - if (!hcon || hcon->state != BT_CONNECTED) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_UNKNOWN_CONN_ID); + if (!hcon || hcon->state != BT_CONNECTED) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_UNKNOWN_CONN_ID); + goto unlock; + } - if (max > hcon->le_conn_max_interval) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_INVALID_LL_PARAMS); + if (max > hcon->le_conn_max_interval) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + goto unlock; + } - if (hci_check_conn_params(min, max, latency, timeout)) - return send_conn_param_neg_reply(hdev, handle, - HCI_ERROR_INVALID_LL_PARAMS); + if (hci_check_conn_params(min, max, latency, timeout)) { + send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + goto unlock; + } if (hcon->role == HCI_ROLE_MASTER) { struct hci_conn_params *params; u8 store_hint; - hci_dev_lock(hdev); - params = hci_conn_params_lookup(hdev, &hcon->dst, hcon->dst_type); if (params) { @@ -6815,8 +6821,6 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, store_hint = 0x00; } - hci_dev_unlock(hdev); - mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, store_hint, min, max, latency, timeout); } @@ -6830,6 +6834,9 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data, cp.max_ce_len = 0; hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp); + +unlock: + hci_dev_unlock(hdev); } static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data, From bda93eec78cdbfe5cda00785cefebd443e56b88b Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Wed, 1 Apr 2026 22:25:26 +0800 Subject: [PATCH 332/700] Bluetooth: MGMT: validate mesh send advertising payload length mesh_send() currently bounds MGMT_OP_MESH_SEND by total command length, but it never verifies that the bytes supplied for the flexible adv_data[] array actually match the embedded adv_data_len field. MGMT_MESH_SEND_SIZE only covers the fixed header, so a truncated command can still pass the existing 20..50 byte range check and later drive the async mesh send path past the end of the queued command buffer. Keep rejecting zero-length and oversized advertising payloads, but validate adv_data_len explicitly and require the command length to exactly match the flexible array size before queueing the request. Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh") Reported-by: Keenan Dong Signed-off-by: Keenan Dong Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index adcd86c15b4e63..b05bb380e5f877 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2478,6 +2478,7 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) struct mgmt_mesh_tx *mesh_tx; struct mgmt_cp_mesh_send *send = data; struct mgmt_rp_mesh_read_features rp; + u16 expected_len; bool sending; int err = 0; @@ -2485,12 +2486,19 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) !hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, MGMT_STATUS_NOT_SUPPORTED); - if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) || - len <= MGMT_MESH_SEND_SIZE || - len > (MGMT_MESH_SEND_SIZE + 31)) + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, + MGMT_STATUS_REJECTED); + + if (!send->adv_data_len || send->adv_data_len > 31) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, MGMT_STATUS_REJECTED); + expected_len = struct_size(send, adv_data, send->adv_data_len); + if (expected_len != len) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); memset(&rp, 0, sizeof(rp)); From d05111bfe37bfd8bd4d2dfe6675d6bdeef43f7c7 Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Tue, 31 Mar 2026 11:52:12 +0000 Subject: [PATCH 333/700] Bluetooth: SMP: force responder MITM requirements before building the pairing response smp_cmd_pairing_req() currently builds the pairing response from the initiator auth_req before enforcing the local BT_SECURITY_HIGH requirement. If the initiator omits SMP_AUTH_MITM, the response can also omit it even though the local side still requires MITM. tk_request() then sees an auth value without SMP_AUTH_MITM and may select JUST_CFM, making method selection inconsistent with the pairing policy the responder already enforces. When the local side requires HIGH security, first verify that MITM can be achieved from the IO capabilities and then force SMP_AUTH_MITM in the response in both rsp.auth_req and auth. This keeps the responder auth bits and later method selection aligned. Fixes: 2b64d153a0cc ("Bluetooth: Add MITM mechanism to LE-SMP") Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/smp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 485e3468bd26a0..deb8dd244b77bd 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1826,7 +1826,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (sec_level > conn->hcon->pending_sec_level) conn->hcon->pending_sec_level = sec_level; - /* If we need MITM check that it can be achieved */ + /* If we need MITM check that it can be achieved. */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { u8 method; @@ -1834,6 +1834,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) req->io_capability); if (method == JUST_WORKS || method == JUST_CFM) return SMP_AUTH_REQUIREMENTS; + + /* Force MITM bit if it isn't set by the initiator. */ + auth |= SMP_AUTH_MITM; + rsp.auth_req |= SMP_AUTH_MITM; } key_size = min(req->max_key_size, rsp.max_key_size); From 20756fec2f0108cb88e815941f1ffff88dc286fe Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Tue, 31 Mar 2026 11:52:13 +0000 Subject: [PATCH 334/700] Bluetooth: SMP: derive legacy responder STK authentication from MITM state The legacy responder path in smp_random() currently labels the stored STK as authenticated whenever pending_sec_level is BT_SECURITY_HIGH. That reflects what the local service requested, not what the pairing flow actually achieved. For Just Works/Confirm legacy pairing, SMP_FLAG_MITM_AUTH stays clear and the resulting STK should remain unauthenticated even if the local side requested HIGH security. Use the established MITM state when storing the responder STK so the key metadata matches the pairing result. This also keeps the legacy path aligned with the Secure Connections code, which already treats JUST_WORKS/JUST_CFM as unauthenticated. Fixes: fff3490f4781 ("Bluetooth: Fix setting correct authentication information for SMP STK") Cc: stable@vger.kernel.org Signed-off-by: Oleh Konko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/smp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index deb8dd244b77bd..98f1da4f5f559b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1018,10 +1018,7 @@ static u8 smp_random(struct smp_chan *smp) smp_s1(smp->tk, smp->prnd, smp->rrnd, stk); - if (hcon->pending_sec_level == BT_SECURITY_HIGH) - auth = 1; - else - auth = 0; + auth = test_bit(SMP_FLAG_MITM_AUTH, &smp->flags) ? 1 : 0; /* Even though there's no _RESPONDER suffix this is the * responder STK we're adding for later lookup (the initiator From bc39a094730ce062fa034a529c93147c096cb488 Mon Sep 17 00:00:00 2001 From: hkbinbin Date: Tue, 31 Mar 2026 05:39:16 +0000 Subject: [PATCH 335/700] Bluetooth: hci_sync: fix stack buffer overflow in hci_le_big_create_sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hci_le_big_create_sync() uses DEFINE_FLEX to allocate a struct hci_cp_le_big_create_sync on the stack with room for 0x11 (17) BIS entries. However, conn->num_bis can hold up to HCI_MAX_ISO_BIS (31) entries — validated against ISO_MAX_NUM_BIS (0x1f) in the caller hci_conn_big_create_sync(). When conn->num_bis is between 18 and 31, the memcpy that copies conn->bis into cp->bis writes up to 14 bytes past the stack buffer, corrupting adjacent stack memory. This is trivially reproducible: binding an ISO socket with bc_num_bis = ISO_MAX_NUM_BIS (31) and calling listen() will eventually trigger hci_le_big_create_sync() from the HCI command sync worker, causing a KASAN-detectable stack-out-of-bounds write: BUG: KASAN: stack-out-of-bounds in hci_le_big_create_sync+0x256/0x3b0 Write of size 31 at addr ffffc90000487b48 by task kworker/u9:0/71 Fix this by changing the DEFINE_FLEX count from the incorrect 0x11 to HCI_MAX_ISO_BIS, which matches the maximum number of BIS entries that conn->bis can actually carry. Fixes: 42ecf1947135 ("Bluetooth: ISO: Do not emit LE BIG Create Sync if previous is pending") Cc: stable@vger.kernel.org Signed-off-by: hkbinbin Reviewed-by: Paul Menzel Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ffb0ceda6f7b58..919ec275dd237d 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7241,7 +7241,8 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) { - DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, 0x11); + DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, + HCI_MAX_ISO_BIS); struct hci_conn *conn = data; struct bt_iso_qos *qos = &conn->iso_qos; int err; From 39ed7d89b973329cc5c764b65ba6302b17b1907e Mon Sep 17 00:00:00 2001 From: Anthony Pighin Date: Tue, 31 Mar 2026 14:26:32 -0400 Subject: [PATCH 336/700] i2c: imx: zero-initialize dma_slave_config for eDMA commit 66d88e16f204 ("dmaengine: fsl-edma: read/write multiple registers in cyclic transactions") causes fsl_edma_fill_tcd() to read dst_port_window_size and src_port_window_size when building transfer control descriptors. Initialize the structure so unset fields are explicitly zero. Fixes: 66d88e16f204 ("dmaengine: fsl-edma: read/write multiple registers in cyclic transactions") Signed-off-by: Anthony Pighin Cc: # v6.14+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260331182632.888110-1-anthony.pighin@nokia.com --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 452d120a210b13..a208fefd3c3b35 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -401,7 +401,7 @@ static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx) static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_addr_t phy_addr) { struct imx_i2c_dma *dma; - struct dma_slave_config dma_sconfig; + struct dma_slave_config dma_sconfig = {}; struct device *dev = i2c_imx->adapter.dev.parent; int ret; From ffb5a4843c5bde702ed17cbcdbda98b37f7a6dad Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Tue, 31 Mar 2026 12:17:18 +0800 Subject: [PATCH 337/700] ipv6: fix data race in fib6_metric_set() using cmpxchg fib6_metric_set() may be called concurrently from softirq context without holding the FIB table lock. A typical path is: ndisc_router_discovery() spin_unlock_bh(&table->tb6_lock) <- lock released fib6_metric_set(rt, RTAX_HOPLIMIT, ...) <- lockless call When two CPUs process Router Advertisement packets for the same router simultaneously, they can both arrive at fib6_metric_set() with the same fib6_info pointer whose fib6_metrics still points to dst_default_metrics. if (f6i->fib6_metrics == &dst_default_metrics) { /* both CPUs: true */ struct dst_metrics *p = kzalloc_obj(*p, GFP_ATOMIC); refcount_set(&p->refcnt, 1); f6i->fib6_metrics = p; /* CPU1 overwrites CPU0's p -> p0 leaked */ } The dst_metrics allocated by the losing CPU has refcnt=1 but no pointer to it anywhere in memory, producing a kmemleak report: unreferenced object 0xff1100025aca1400 (size 96): comm "softirq", pid 0, jiffies 4299271239 backtrace: kmalloc_trace+0x28a/0x380 fib6_metric_set+0xcd/0x180 ndisc_router_discovery+0x12dc/0x24b0 icmpv6_rcv+0xc16/0x1360 Fix this by: - Set val for p->metrics before published via cmpxchg() so the metrics value is ready before the pointer becomes visible to other CPUs. - Replace the plain pointer store with cmpxchg() and free the allocation safely when competition failed. - Add READ_ONCE()/WRITE_ONCE() for metrics[] setting in the non-default metrics path to prevent compiler-based data races. Fixes: d4ead6b34b67 ("net/ipv6: move metrics from dst to rt6_info") Reported-by: Fei Liu Reviewed-by: Jiayuan Chen Signed-off-by: Hangbin Liu Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260331-b4-fib6_metric_set-kmemleak-v3-1-88d27f4d8825@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_fib.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index dd26657b6a4acd..45ef4d65dcbc70 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -727,20 +727,28 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val) { + struct dst_metrics *m; + if (!f6i) return; - if (f6i->fib6_metrics == &dst_default_metrics) { + if (READ_ONCE(f6i->fib6_metrics) == &dst_default_metrics) { + struct dst_metrics *dflt = (struct dst_metrics *)&dst_default_metrics; struct dst_metrics *p = kzalloc_obj(*p, GFP_ATOMIC); if (!p) return; + p->metrics[metric - 1] = val; refcount_set(&p->refcnt, 1); - f6i->fib6_metrics = p; + if (cmpxchg(&f6i->fib6_metrics, dflt, p) != dflt) + kfree(p); + else + return; } - f6i->fib6_metrics->metrics[metric - 1] = val; + m = READ_ONCE(f6i->fib6_metrics); + WRITE_ONCE(m->metrics[metric - 1], val); } /* From a54ecccfae62c5c85259ae5ea5d9c20009519049 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Tue, 31 Mar 2026 00:32:38 +0800 Subject: [PATCH 338/700] rds: ib: reject FRMR registration before IB connection is established rds_ib_get_mr() extracts the rds_ib_connection from conn->c_transport_data and passes it to rds_ib_reg_frmr() for FRWR memory registration. On a fresh outgoing connection, ic is allocated in rds_ib_conn_alloc() with i_cm_id = NULL because the connection worker has not yet called rds_ib_conn_path_connect() to create the rdma_cm_id. When sendmsg() with RDS_CMSG_RDMA_MAP is called on such a connection, the sendmsg path parses the control message before any connection establishment, allowing rds_ib_post_reg_frmr() to dereference ic->i_cm_id->qp and crash the kernel. The existing guard in rds_ib_reg_frmr() only checks for !ic (added in commit 9e630bcb7701), which does not catch this case since ic is allocated early and is always non-NULL once the connection object exists. KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:rds_ib_post_reg_frmr+0x50e/0x920 Call Trace: rds_ib_post_reg_frmr (net/rds/ib_frmr.c:167) rds_ib_map_frmr (net/rds/ib_frmr.c:252) rds_ib_reg_frmr (net/rds/ib_frmr.c:430) rds_ib_get_mr (net/rds/ib_rdma.c:615) __rds_rdma_map (net/rds/rdma.c:295) rds_cmsg_rdma_map (net/rds/rdma.c:860) rds_sendmsg (net/rds/send.c:1363) ____sys_sendmsg do_syscall_64 Add a check in rds_ib_get_mr() that verifies ic, i_cm_id, and qp are all non-NULL before proceeding with FRMR registration, mirroring the guard already present in rds_ib_post_inv(). Return -ENODEV when the connection is not ready, which the existing error handling in rds_cmsg_send() converts to -EAGAIN for userspace retry and triggers rds_conn_connect_if_down() to start the connection worker. Fixes: 1659185fb4d0 ("RDS: IB: Support Fastreg MR (FRMR) memory registration mode") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260330163237.2752440-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/ib_rdma.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 077f7041df1556..2cfec252eeac2f 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -604,8 +604,13 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, return ibmr; } - if (conn) + if (conn) { ic = conn->c_transport_data; + if (!ic || !ic->i_cm_id || !ic->i_cm_id->qp) { + ret = -ENODEV; + goto out; + } + } if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) { ret = -ENODEV; From ad8391d37f334ee73ba91926f8b4e4cf6d31ea04 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 1 Apr 2026 00:54:15 +0000 Subject: [PATCH 339/700] bpf: sockmap: Fix use-after-free of sk->sk_socket in sk_psock_verdict_data_ready(). syzbot reported use-after-free of AF_UNIX socket's sk->sk_socket in sk_psock_verdict_data_ready(). [0] In unix_stream_sendmsg(), the peer socket's ->sk_data_ready() is called after dropping its unix_state_lock(). Although the sender socket holds the peer's refcount, it does not prevent the peer's sock_orphan(), and the peer's sk_socket might be freed after one RCU grace period. Let's fetch the peer's sk->sk_socket and sk->sk_socket->ops under RCU in sk_psock_verdict_data_ready(). [0]: BUG: KASAN: slab-use-after-free in sk_psock_verdict_data_ready+0xec/0x590 net/core/skmsg.c:1278 Read of size 8 at addr ffff8880594da860 by task syz.4.1842/11013 CPU: 1 UID: 0 PID: 11013 Comm: syz.4.1842 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xba/0x230 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 sk_psock_verdict_data_ready+0xec/0x590 net/core/skmsg.c:1278 unix_stream_sendmsg+0x8a3/0xe80 net/unix/af_unix.c:2482 sock_sendmsg_nosec net/socket.c:721 [inline] __sock_sendmsg net/socket.c:736 [inline] ____sys_sendmsg+0x972/0x9f0 net/socket.c:2585 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2639 __sys_sendmsg net/socket.c:2671 [inline] __do_sys_sendmsg net/socket.c:2676 [inline] __se_sys_sendmsg net/socket.c:2674 [inline] __x64_sys_sendmsg+0x1bd/0x2a0 net/socket.c:2674 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7facf899c819 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007facf9827028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 00007facf8c15fa0 RCX: 00007facf899c819 RDX: 0000000000000000 RSI: 0000200000000500 RDI: 0000000000000004 RBP: 00007facf8a32c91 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007facf8c16038 R14: 00007facf8c15fa0 R15: 00007ffd41b01c78 Allocated by task 11013: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 unpoison_slab_object mm/kasan/common.c:340 [inline] __kasan_slab_alloc+0x6c/0x80 mm/kasan/common.c:366 kasan_slab_alloc include/linux/kasan.h:253 [inline] slab_post_alloc_hook mm/slub.c:4538 [inline] slab_alloc_node mm/slub.c:4866 [inline] kmem_cache_alloc_lru_noprof+0x2b8/0x640 mm/slub.c:4885 sock_alloc_inode+0x28/0xc0 net/socket.c:316 alloc_inode+0x6a/0x1b0 fs/inode.c:347 new_inode_pseudo include/linux/fs.h:3003 [inline] sock_alloc net/socket.c:631 [inline] __sock_create+0x12d/0x9d0 net/socket.c:1562 sock_create net/socket.c:1656 [inline] __sys_socketpair+0x1c4/0x560 net/socket.c:1803 __do_sys_socketpair net/socket.c:1856 [inline] __se_sys_socketpair net/socket.c:1853 [inline] __x64_sys_socketpair+0x9b/0xb0 net/socket.c:1853 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 15: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6165 [inline] kmem_cache_free+0x187/0x630 mm/slub.c:6295 rcu_do_batch kernel/rcu/tree.c:2617 [inline] rcu_core+0x7cd/0x1070 kernel/rcu/tree.c:2869 handle_softirqs+0x22a/0x870 kernel/softirq.c:622 run_ksoftirqd+0x36/0x60 kernel/softirq.c:1063 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x51e/0xb90 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: c63829182c37 ("af_unix: Implement ->psock_update_sk_prot()") Closes: https://lore.kernel.org/bpf/69cc6b9f.a70a0220.128fd0.004b.GAE@google.com/ Reported-by: syzbot+2184232f07e3677fbaef@syzkaller.appspotmail.com Signed-off-by: Kuniyuki Iwashima Signed-off-by: Martin KaFai Lau Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260401005418.2452999-1-kuniyu@google.com --- net/core/skmsg.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 3261793abe8332..6187a83bd7411b 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1267,17 +1267,20 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) static void sk_psock_verdict_data_ready(struct sock *sk) { - struct socket *sock = sk->sk_socket; - const struct proto_ops *ops; + const struct proto_ops *ops = NULL; + struct socket *sock; int copied; trace_sk_data_ready(sk); - if (unlikely(!sock)) - return; - ops = READ_ONCE(sock->ops); + rcu_read_lock(); + sock = READ_ONCE(sk->sk_socket); + if (likely(sock)) + ops = READ_ONCE(sock->ops); + rcu_read_unlock(); if (!ops || !ops->read_skb) return; + copied = ops->read_skb(sk, sk_psock_verdict_recv); if (copied >= 0) { struct sk_psock *psock; From d64cb81dcbd54927515a7f65e5e24affdc73c14b Mon Sep 17 00:00:00 2001 From: Yucheng Lu Date: Tue, 31 Mar 2026 16:00:21 +0800 Subject: [PATCH 340/700] net/sched: sch_netem: fix out-of-bounds access in packet corruption In netem_enqueue(), the packet corruption logic uses get_random_u32_below(skb_headlen(skb)) to select an index for modifying skb->data. When an AF_PACKET TX_RING sends fully non-linear packets over an IPIP tunnel, skb_headlen(skb) evaluates to 0. Passing 0 to get_random_u32_below() takes the variable-ceil slow path which returns an unconstrained 32-bit random integer. Using this unconstrained value as an offset into skb->data results in an out-of-bounds memory access. Fix this by verifying skb_headlen(skb) is non-zero before attempting to corrupt the linear data area. Fully non-linear packets will silently bypass the corruption logic. Fixes: c865e5d99e25 ("[PKT_SCHED] netem: packet corruption option") Reported-by: Yifan Wu Reported-by: Juefei Pu Signed-off-by: Yuan Tan Signed-off-by: Xin Liu Signed-off-by: Yuhang Zheng Signed-off-by: Yucheng Lu Reviewed-by: Stephen Hemminger Link: https://patch.msgid.link/45435c0935df877853a81e6d06205ac738ec65fa.1774941614.git.kanolyc@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/sch_netem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5de1c932944a7c..20df1c08b1e9d0 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -519,8 +519,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, goto finish_segs; } - skb->data[get_random_u32_below(skb_headlen(skb))] ^= - 1<data[get_random_u32_below(skb_headlen(skb))] ^= + 1 << get_random_u32_below(8); } if (unlikely(q->t_len >= sch->limit)) { From b4e5f04c58a29c499faa85d12952ca9a4faf1cb9 Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Thu, 26 Mar 2026 19:53:44 +0530 Subject: [PATCH 341/700] virtio_net: clamp rss_max_key_size to NETDEV_RSS_KEY_LEN rss_max_key_size in the virtio spec is the maximum key size supported by the device, not a mandatory size the driver must use. Also the value 40 is a spec minimum, not a spec maximum. The current code rejects RSS and can fail probe when the device reports a larger rss_max_key_size than the driver buffer limit. Instead, clamp the effective key length to min(device rss_max_key_size, NETDEV_RSS_KEY_LEN) and keep RSS enabled. This keeps probe working on devices that advertise larger maximum key sizes while respecting the netdev RSS key buffer size limit. Fixes: 3f7d9c1964fc ("virtio_net: Add hash_key_length check") Cc: stable@vger.kernel.org Signed-off-by: Srujana Challa Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260326142344.1171317-1-schalla@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ab2108ee206a91..c0b9bc5574e232 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -381,8 +381,6 @@ struct receive_queue { struct xdp_buff **xsk_buffs; }; -#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40 - /* Control VQ buffers: protected by the rtnl lock */ struct control_buf { struct virtio_net_ctrl_hdr hdr; @@ -486,7 +484,7 @@ struct virtnet_info { /* Must be last as it ends in a flexible-array member. */ TRAILING_OVERLAP(struct virtio_net_rss_config_trailer, rss_trailer, hash_key_data, - u8 rss_hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE]; + u8 rss_hash_key_data[NETDEV_RSS_KEY_LEN]; ); }; static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) == @@ -6708,6 +6706,7 @@ static int virtnet_probe(struct virtio_device *vdev) struct virtnet_info *vi; u16 max_queue_pairs; int mtu = 0; + u16 key_sz; /* Find if host supports multiqueue/rss virtio_net device */ max_queue_pairs = 1; @@ -6842,14 +6841,13 @@ static int virtnet_probe(struct virtio_device *vdev) } if (vi->has_rss || vi->has_rss_hash_report) { - vi->rss_key_size = - virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); - if (vi->rss_key_size > VIRTIO_NET_RSS_MAX_KEY_SIZE) { - dev_err(&vdev->dev, "rss_max_key_size=%u exceeds the limit %u.\n", - vi->rss_key_size, VIRTIO_NET_RSS_MAX_KEY_SIZE); - err = -EINVAL; - goto free; - } + key_sz = virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); + + vi->rss_key_size = min_t(u16, key_sz, NETDEV_RSS_KEY_LEN); + if (key_sz > vi->rss_key_size) + dev_warn(&vdev->dev, + "rss_max_key_size=%u exceeds driver limit %u, clamping\n", + key_sz, vi->rss_key_size); vi->rss_hash_types_supported = virtio_cread32(vdev, offsetof(struct virtio_net_config, supported_hash_types)); From ce8fe5287b87e24e225c342f3b0ec04f0b3680fe Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 30 Mar 2026 21:45:40 +0300 Subject: [PATCH 342/700] net: macb: fix clk handling on PCI glue driver removal platform_device_unregister() may still want to use the registered clks during runtime resume callback. Note that there is a commit d82d5303c4c5 ("net: macb: fix use after free on rmmod") that addressed the similar problem of clk vs platform device unregistration but just moved the bug to another place. Save the pointers to clks into local variables for reuse after platform device is unregistered. BUG: KASAN: use-after-free in clk_prepare+0x5a/0x60 Read of size 8 at addr ffff888104f85e00 by task modprobe/597 CPU: 2 PID: 597 Comm: modprobe Not tainted 6.1.164+ #114 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.1-0-g3208b098f51a-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x8d/0xba print_report+0x17f/0x496 kasan_report+0xd9/0x180 clk_prepare+0x5a/0x60 macb_runtime_resume+0x13d/0x410 [macb] pm_generic_runtime_resume+0x97/0xd0 __rpm_callback+0xc8/0x4d0 rpm_callback+0xf6/0x230 rpm_resume+0xeeb/0x1a70 __pm_runtime_resume+0xb4/0x170 bus_remove_device+0x2e3/0x4b0 device_del+0x5b3/0xdc0 platform_device_del+0x4e/0x280 platform_device_unregister+0x11/0x50 pci_device_remove+0xae/0x210 device_remove+0xcb/0x180 device_release_driver_internal+0x529/0x770 driver_detach+0xd4/0x1a0 bus_remove_driver+0x135/0x260 driver_unregister+0x72/0xb0 pci_unregister_driver+0x26/0x220 __do_sys_delete_module+0x32e/0x550 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Allocated by task 519: kasan_save_stack+0x2c/0x50 kasan_set_track+0x21/0x30 __kasan_kmalloc+0x8e/0x90 __clk_register+0x458/0x2890 clk_hw_register+0x1a/0x60 __clk_hw_register_fixed_rate+0x255/0x410 clk_register_fixed_rate+0x3c/0xa0 macb_probe+0x1d8/0x42e [macb_pci] local_pci_probe+0xd7/0x190 pci_device_probe+0x252/0x600 really_probe+0x255/0x7f0 __driver_probe_device+0x1ee/0x330 driver_probe_device+0x4c/0x1f0 __driver_attach+0x1df/0x4e0 bus_for_each_dev+0x15d/0x1f0 bus_add_driver+0x486/0x5e0 driver_register+0x23a/0x3d0 do_one_initcall+0xfd/0x4d0 do_init_module+0x18b/0x5a0 load_module+0x5663/0x7950 __do_sys_finit_module+0x101/0x180 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 597: kasan_save_stack+0x2c/0x50 kasan_set_track+0x21/0x30 kasan_save_free_info+0x2a/0x50 __kasan_slab_free+0x106/0x180 __kmem_cache_free+0xbc/0x320 clk_unregister+0x6de/0x8d0 macb_remove+0x73/0xc0 [macb_pci] pci_device_remove+0xae/0x210 device_remove+0xcb/0x180 device_release_driver_internal+0x529/0x770 driver_detach+0xd4/0x1a0 bus_remove_driver+0x135/0x260 driver_unregister+0x72/0xb0 pci_unregister_driver+0x26/0x220 __do_sys_delete_module+0x32e/0x550 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Fixes: d82d5303c4c5 ("net: macb: fix use after free on rmmod") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260330184542.626619-1-pchelkin@ispras.ru Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index fc4f5aee6ab3f6..0ce5b736ea4389 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -109,10 +109,12 @@ static void macb_remove(struct pci_dev *pdev) { struct platform_device *plat_dev = pci_get_drvdata(pdev); struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev); + struct clk *pclk = plat_data->pclk; + struct clk *hclk = plat_data->hclk; - clk_unregister(plat_data->pclk); - clk_unregister(plat_data->hclk); platform_device_unregister(plat_dev); + clk_unregister(pclk); + clk_unregister(hclk); } static const struct pci_device_id dev_id_table[] = { From f0f367a4f459cc8118aadc43c6bba53c60d93f8d Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 30 Mar 2026 21:45:41 +0300 Subject: [PATCH 343/700] net: macb: properly unregister fixed rate clocks The additional resources allocated with clk_register_fixed_rate() need to be released with clk_unregister_fixed_rate(), otherwise they are lost. Fixes: 83a77e9ec415 ("net: macb: Added PCI wrapper for Platform Driver.") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260330184542.626619-2-pchelkin@ispras.ru Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 0ce5b736ea4389..b79dec17e6b099 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -96,10 +96,10 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_plat_dev_register: - clk_unregister(plat_data.hclk); + clk_unregister_fixed_rate(plat_data.hclk); err_hclk_register: - clk_unregister(plat_data.pclk); + clk_unregister_fixed_rate(plat_data.pclk); err_pclk_register: return err; @@ -113,8 +113,8 @@ static void macb_remove(struct pci_dev *pdev) struct clk *hclk = plat_data->hclk; platform_device_unregister(plat_dev); - clk_unregister(pclk); - clk_unregister(hclk); + clk_unregister_fixed_rate(pclk); + clk_unregister_fixed_rate(hclk); } static const struct pci_device_id dev_id_table[] = { From bf16bca6653679d8a514d6c1c5a2c67065033f14 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 30 Mar 2026 22:40:13 +0300 Subject: [PATCH 344/700] net/mlx5: lag: Check for LAG device before creating debugfs __mlx5_lag_dev_add_mdev() may return 0 (success) even when an error occurs that is handled gracefully. Consequently, the initialization flow proceeds to call mlx5_ldev_add_debugfs() even when there is no valid LAG context. mlx5_ldev_add_debugfs() blindly created the debugfs directory and attributes. This exposed interfaces (like the members file) that rely on a valid ldev pointer, leading to potential NULL pointer dereferences if accessed when ldev is NULL. Add a check to verify that mlx5_lag_dev(dev) returns a valid pointer before attempting to create the debugfs entries. Fixes: 7f46a0b7327a ("net/mlx5: Lag, add debugfs to query hardware lag state") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c index 62b6faa4276aad..b8d5f6a44d26ab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c @@ -160,8 +160,11 @@ DEFINE_SHOW_ATTRIBUTE(members); void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev) { + struct mlx5_lag *ldev = mlx5_lag_dev(dev); struct dentry *dbg; + if (!ldev) + return; dbg = debugfs_create_dir("lag", mlx5_debugfs_get_dev_root(dev)); dev->priv.dbg.lag_debugfs = dbg; From 10dc35f6a443d488f219d1a1e3fb8f8dac422070 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 30 Mar 2026 22:40:14 +0300 Subject: [PATCH 345/700] net/mlx5: Avoid "No data available" when FW version queries fail Avoid printing the misleading "kernel answers: No data available" devlink output when querying firmware or pending firmware version fails (e.g. MLX5 fw state errors / flash failures). FW can fail on loading the pending flash image and get its version due to various reasons, examples: mlxfw: Firmware flash failed: key not applicable, err (7) mlx5_fw_image_pending: can't read pending fw version while fw state is 1 and the resulting: $ devlink dev info kernel answers: No data available Instead, just report 0 or 0xfff.. versions in case of failure to indicate a problem, and let other information be shown. after the fix: $ devlink dev info pci/0000:00:06.0: driver mlx5_core serial_number xxx... board.serial_number MT2225300179 versions: fixed: fw.psid MT_0000000436 running: fw.version 22.41.0188 fw 22.41.0188 stored: fw.version 255.255.65535 fw 255.255.65535 Fixes: 9c86b07e3069 ("net/mlx5: Added fw version query command") Signed-off-by: Saeed Mahameed Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-3-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/devlink.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 53 ++++++++++++------- .../ethernet/mellanox/mlx5/core/mlx5_core.h | 4 +- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 6698ac55a4bfa5..73cf0321bb86ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -107,9 +107,7 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; - err = mlx5_fw_version_query(dev, &running_fw, &stored_fw); - if (err) - return err; + mlx5_fw_version_query(dev, &running_fw, &stored_fw); snprintf(version_str, sizeof(version_str), "%d.%d.%04d", mlx5_fw_ver_major(running_fw), mlx5_fw_ver_minor(running_fw), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index eeb4437975f20a..c1f220e5fe1857 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -822,48 +822,63 @@ mlx5_fw_image_pending(struct mlx5_core_dev *dev, return 0; } -int mlx5_fw_version_query(struct mlx5_core_dev *dev, - u32 *running_ver, u32 *pending_ver) +void mlx5_fw_version_query(struct mlx5_core_dev *dev, + u32 *running_ver, u32 *pending_ver) { u32 reg_mcqi_version[MLX5_ST_SZ_DW(mcqi_version)] = {}; bool pending_version_exists; int component_index; int err; + *running_ver = 0; + *pending_ver = 0; + if (!MLX5_CAP_GEN(dev, mcam_reg) || !MLX5_CAP_MCAM_REG(dev, mcqi) || !MLX5_CAP_MCAM_REG(dev, mcqs)) { mlx5_core_warn(dev, "fw query isn't supported by the FW\n"); - return -EOPNOTSUPP; + return; } component_index = mlx5_get_boot_img_component_index(dev); - if (component_index < 0) - return component_index; + if (component_index < 0) { + mlx5_core_warn(dev, "fw query failed to find boot img component index, err %d\n", + component_index); + return; + } + *running_ver = U32_MAX; /* indicate failure */ err = mlx5_reg_mcqi_version_query(dev, component_index, MCQI_FW_RUNNING_VERSION, reg_mcqi_version); - if (err) - return err; - - *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); - + if (!err) + *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, + version); + else + mlx5_core_warn(dev, "failed to query running version, err %d\n", + err); + + *pending_ver = U32_MAX; /* indicate failure */ err = mlx5_fw_image_pending(dev, component_index, &pending_version_exists); - if (err) - return err; + if (err) { + mlx5_core_warn(dev, "failed to query pending image, err %d\n", + err); + return; + } if (!pending_version_exists) { *pending_ver = 0; - return 0; + return; } err = mlx5_reg_mcqi_version_query(dev, component_index, MCQI_FW_STORED_VERSION, reg_mcqi_version); - if (err) - return err; - - *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); - - return 0; + if (!err) + *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, + version); + else + mlx5_core_warn(dev, "failed to query pending version, err %d\n", + err); + + return; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index b635b423d9721e..1507e881d962ac 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -393,8 +393,8 @@ int mlx5_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw, struct netlink_ext_ack *extack); -int mlx5_fw_version_query(struct mlx5_core_dev *dev, - u32 *running_ver, u32 *stored_ver); +void mlx5_fw_version_query(struct mlx5_core_dev *dev, u32 *running_ver, + u32 *stored_ver); #ifdef CONFIG_MLX5_CORE_EN int mlx5e_init(void); From 403186400a1a6166efe7031edc549c15fee4723f Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 30 Mar 2026 22:40:15 +0300 Subject: [PATCH 346/700] net/mlx5: Fix switchdev mode rollback in case of failure If for some internal reason switchdev mode fails, we rollback to legacy mode, before this patch, rollback will unregister the uplink netdev and leave it unregistered causing the below kernel bug. To fix this, we need to avoid netdev unregister by setting the proper rollback flag 'MLX5_PRIV_FLAGS_SWITCH_LEGACY' to indicate legacy mode. devlink (431) used greatest stack depth: 11048 bytes left mlx5_core 0000:00:03.0: E-Switch: Disable: mode(LEGACY), nvfs(0), \ necvfs(0), active vports(0) mlx5_core 0000:00:03.0: E-Switch: Supported tc chains and prios offload mlx5_core 0000:00:03.0: Loading uplink representor for vport 65535 mlx5_core 0000:00:03.0: mlx5_cmd_out_err:816:(pid 456): \ QUERY_HCA_CAP(0x100) op_mod(0x0) failed, \ status bad parameter(0x3), syndrome (0x3a3846), err(-22) mlx5_core 0000:00:03.0 enp0s3np0 (unregistered): Unloading uplink \ representor for vport 65535 ------------[ cut here ]------------ kernel BUG at net/core/dev.c:12070! Oops: invalid opcode: 0000 [#1] SMP NOPTI CPU: 2 UID: 0 PID: 456 Comm: devlink Not tainted 6.16.0-rc3+ \ #9 PREEMPT(voluntary) RIP: 0010:unregister_netdevice_many_notify+0x123/0xae0 ... Call Trace: [ 90.923094] unregister_netdevice_queue+0xad/0xf0 [ 90.923323] unregister_netdev+0x1c/0x40 [ 90.923522] mlx5e_vport_rep_unload+0x61/0xc6 [ 90.923736] esw_offloads_enable+0x8e6/0x920 [ 90.923947] mlx5_eswitch_enable_locked+0x349/0x430 [ 90.924182] ? is_mp_supported+0x57/0xb0 [ 90.924376] mlx5_devlink_eswitch_mode_set+0x167/0x350 [ 90.924628] devlink_nl_eswitch_set_doit+0x6f/0xf0 [ 90.924862] genl_family_rcv_msg_doit+0xe8/0x140 [ 90.925088] genl_rcv_msg+0x18b/0x290 [ 90.925269] ? __pfx_devlink_nl_pre_doit+0x10/0x10 [ 90.925506] ? __pfx_devlink_nl_eswitch_set_doit+0x10/0x10 [ 90.925766] ? __pfx_devlink_nl_post_doit+0x10/0x10 [ 90.926001] ? __pfx_genl_rcv_msg+0x10/0x10 [ 90.926206] netlink_rcv_skb+0x52/0x100 [ 90.926393] genl_rcv+0x28/0x40 [ 90.926557] netlink_unicast+0x27d/0x3d0 [ 90.926749] netlink_sendmsg+0x1f7/0x430 [ 90.926942] __sys_sendto+0x213/0x220 [ 90.927127] ? __sys_recvmsg+0x6a/0xd0 [ 90.927312] __x64_sys_sendto+0x24/0x30 [ 90.927504] do_syscall_64+0x50/0x1c0 [ 90.927687] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 90.927929] RIP: 0033:0x7f7d0363e047 Fixes: 2a4f56fbcc47 ("net/mlx5e: Keep netdev when leave switchdev for devlink set legacy only") Signed-off-by: Saeed Mahameed Reviewed-by: Jianbo Liu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260330194015.53585-4-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 7a9ee36b8dcaa3..01f6aecc4fccb3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3761,6 +3761,8 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) return 0; err_vports: + /* rollback to legacy, indicates don't unregister the uplink netdev */ + esw->dev->priv.flags |= MLX5_PRIV_FLAGS_SWITCH_LEGACY; mlx5_esw_offloads_rep_unload(esw, MLX5_VPORT_UPLINK); err_uplink: esw_offloads_steering_cleanup(esw); From ceee35e5674aa84cf9e504c2a9dae4587511556c Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 30 Mar 2026 23:51:36 -0700 Subject: [PATCH 347/700] bnxt_en: Refactor some basic ring setup and adjustment logic Refactor out the basic code that trims the default rings, sets up and adjusts XDP TX rings and CP rings. There is no change in behavior. This is to prepare for the next bug fix patch. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-2-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 53 +++++++++++++------ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 5 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 5 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7ed805713fbbe0..173f962fc2abe0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -12992,6 +12992,21 @@ static int bnxt_tx_nr_rings_per_tc(struct bnxt *bp) return bp->num_tc ? bp->tx_nr_rings / bp->num_tc : bp->tx_nr_rings; } +static void bnxt_set_xdp_tx_rings(struct bnxt *bp) +{ + bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc; + bp->tx_nr_rings += bp->tx_nr_rings_xdp; +} + +static void bnxt_adj_tx_rings(struct bnxt *bp) +{ + /* Make adjustments if reserved TX rings are less than requested */ + bp->tx_nr_rings -= bp->tx_nr_rings_xdp; + bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + if (bp->tx_nr_rings_xdp) + bnxt_set_xdp_tx_rings(bp); +} + static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; @@ -13009,13 +13024,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) if (rc) return rc; - /* Make adjustments if reserved TX rings are less than requested */ - bp->tx_nr_rings -= bp->tx_nr_rings_xdp; - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); - if (bp->tx_nr_rings_xdp) { - bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc; - bp->tx_nr_rings += bp->tx_nr_rings_xdp; - } + bnxt_adj_tx_rings(bp); rc = bnxt_alloc_mem(bp, irq_re_init); if (rc) { netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc); @@ -15436,11 +15445,19 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu) return 0; } +void bnxt_set_cp_rings(struct bnxt *bp, bool sh) +{ + int tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); + + bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : + tx_cp + bp->rx_nr_rings; +} + int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) { struct bnxt *bp = netdev_priv(dev); bool sh = false; - int rc, tx_cp; + int rc; if (tc > bp->max_tc) { netdev_err(dev, "Too many traffic classes requested: %d. Max supported is %d.\n", @@ -15473,9 +15490,7 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) bp->num_tc = 0; } bp->tx_nr_rings += bp->tx_nr_rings_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : - tx_cp + bp->rx_nr_rings; + bnxt_set_cp_rings(bp, sh); if (netif_running(bp->dev)) return bnxt_open_nic(bp, true, false); @@ -16525,6 +16540,15 @@ static void bnxt_trim_dflt_sh_rings(struct bnxt *bp) bp->tx_nr_rings = bnxt_tx_nr_rings(bp); } +static void bnxt_adj_dflt_rings(struct bnxt *bp, bool sh) +{ + if (sh) + bnxt_trim_dflt_sh_rings(bp); + else + bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; + bp->tx_nr_rings = bnxt_tx_nr_rings(bp); +} + static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) { int dflt_rings, max_rx_rings, max_tx_rings, rc; @@ -16550,11 +16574,8 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) return rc; bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings); bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings); - if (sh) - bnxt_trim_dflt_sh_rings(bp); - else - bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; - bp->tx_nr_rings = bnxt_tx_nr_rings(bp); + + bnxt_adj_dflt_rings(bp, sh); avail_msix = bnxt_get_max_func_irqs(bp) - bp->cp_nr_rings; if (avail_msix >= BNXT_MIN_ROCE_CP_RINGS) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index a97d651130dfb8..4bc7f7aeaab378 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2985,6 +2985,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, int tx_xdp); int bnxt_fw_init_one(struct bnxt *bp); bool bnxt_hwrm_reset_permitted(struct bnxt *bp); +void bnxt_set_cp_rings(struct bnxt *bp, bool sh); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); struct bnxt_ntuple_filter *bnxt_lookup_ntp_filter_from_idx(struct bnxt *bp, struct bnxt_ntuple_filter *fltr, u32 idx); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 28d0ece2e7b1fb..0407aa1b3190e4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -945,7 +945,6 @@ static int bnxt_set_channels(struct net_device *dev, bool sh = false; int tx_xdp = 0; int rc = 0; - int tx_cp; if (channel->other_count) return -EINVAL; @@ -1013,9 +1012,7 @@ static int bnxt_set_channels(struct net_device *dev, if (tcs > 1) bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs + tx_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) : - tx_cp + bp->rx_nr_rings; + bnxt_set_cp_rings(bp, sh); /* After changing number of rx channels, update NTUPLE feature. */ netdev_update_features(dev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 85cbeb35681c25..bebe37e139c94c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -384,7 +384,7 @@ int bnxt_xdp_xmit(struct net_device *dev, int num_frames, static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) { struct net_device *dev = bp->dev; - int tx_xdp = 0, tx_cp, rc, tc; + int tx_xdp = 0, rc, tc; struct bpf_prog *old; netdev_assert_locked(dev); @@ -431,8 +431,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) } bp->tx_nr_rings_xdp = tx_xdp; bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp; - tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); - bp->cp_nr_rings = max_t(int, tx_cp, bp->rx_nr_rings); + bnxt_set_cp_rings(bp, true); bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); From e4bf81dcad0a6fff2bbe5331d2c7fb30d45a788c Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 30 Mar 2026 23:51:37 -0700 Subject: [PATCH 348/700] bnxt_en: Don't assume XDP is never enabled in bnxt_init_dflt_ring_mode() The original code made the assumption that when we set up the initial default ring mode, we must be just loading the driver and XDP cannot be enabled yet. This is not true when the FW goes through a resource or capability change. Resource reservations will be cancelled and reinitialized with XDP already enabled. devlink reload with XDP enabled will also have the same issue. This scenario will cause the ring arithmetic to be all wrong in the bnxt_init_dflt_ring_mode() path causing failure: bnxt_en 0000:a1:00.0 ens2f0np0: bnxt_setup_int_mode err: ffffffea bnxt_en 0000:a1:00.0 ens2f0np0: bnxt_request_irq err: ffffffea bnxt_en 0000:a1:00.0 ens2f0np0: nic open fail (rc: ffffffea) Fix it by properly accounting for XDP in the bnxt_init_dflt_ring_mode() path by using the refactored helper functions in the previous patch. Reviewed-by: Andy Gospodarek Reviewed-by: Pavan Chebbi Reviewed-by: Kalesh AP Fixes: ec5d31e3c15d ("bnxt_en: Handle firmware reset status during IF_UP.") Fixes: 228ea8c187d8 ("bnxt_en: implement devlink dev reload driver_reinit") Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-3-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 173f962fc2abe0..f11f3a704da54e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -16547,6 +16547,10 @@ static void bnxt_adj_dflt_rings(struct bnxt *bp, bool sh) else bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings; bp->tx_nr_rings = bnxt_tx_nr_rings(bp); + if (sh && READ_ONCE(bp->xdp_prog)) { + bnxt_set_xdp_tx_rings(bp); + bnxt_set_cp_rings(bp, true); + } } static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) @@ -16588,16 +16592,17 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) rc = __bnxt_reserve_rings(bp); if (rc && rc != -ENODEV) netdev_warn(bp->dev, "Unable to reserve tx rings\n"); - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + + bnxt_adj_tx_rings(bp); if (sh) - bnxt_trim_dflt_sh_rings(bp); + bnxt_adj_dflt_rings(bp, true); /* Rings may have been trimmed, re-reserve the trimmed rings. */ if (bnxt_need_reserve_rings(bp)) { rc = __bnxt_reserve_rings(bp); if (rc && rc != -ENODEV) netdev_warn(bp->dev, "2nd rings reservation failed.\n"); - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + bnxt_adj_tx_rings(bp); } if (BNXT_CHIP_TYPE_NITRO_A0(bp)) { bp->rx_nr_rings++; @@ -16631,7 +16636,7 @@ static int bnxt_init_dflt_ring_mode(struct bnxt *bp) if (rc) goto init_dflt_ring_err; - bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp); + bnxt_adj_tx_rings(bp); bnxt_set_dflt_rfs(bp); From 071dbfa304e85a6b04a593e950d18fa170997288 Mon Sep 17 00:00:00 2001 From: Pavan Chebbi Date: Mon, 30 Mar 2026 23:51:38 -0700 Subject: [PATCH 349/700] bnxt_en: Restore default stat ctxs for ULP when resource is available During resource reservation, if the L2 driver does not have enough MSIX vectors to provide to the RoCE driver, it sets the stat ctxs for ULP also to 0 so that we don't have to reserve it unnecessarily. However, subsequently the user may reduce L2 rings thereby freeing up some resources that the L2 driver can now earmark for RoCE. In this case, the driver should restore the default ULP stat ctxs to make sure that all RoCE resources are ready for use. The RoCE driver may fail to initialize in this scenario without this fix. Fixes: d630624ebd70 ("bnxt_en: Utilize ulp client resources if RoCE is not registered") Reviewed-by: Kalesh AP Signed-off-by: Pavan Chebbi Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260331065138.948205-4-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f11f3a704da54e..3f775196ef8194 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8045,6 +8045,8 @@ static int __bnxt_reserve_rings(struct bnxt *bp) ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want); if (!ulp_msix) bnxt_set_ulp_stat_ctxs(bp, 0); + else + bnxt_set_dflt_ulp_stat_ctxs(bp); if (ulp_msix > bp->ulp_num_msix_want) ulp_msix = bp->ulp_num_msix_want; From f8995c2df519f382525ca4bc90553ad2ec611067 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2026 17:42:51 +0100 Subject: [PATCH 350/700] drm/ioc32: stop speculation on the drm_compat_ioctl path The drm compat ioctl path takes a user controlled pointer, and then dereferences it into a table of function pointers, the signature method of spectre problems. Fix this up by calling array_index_nospec() on the index to the function pointer list. Fixes: 505b5240329b ("drm/ioctl: Fix Spectre v1 vulnerabilities") Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: stable Assisted-by: gkh_clanker_2000 Signed-off-by: Greg Kroah-Hartman Acked-by: Thomas Zimmermann Acked-by: Maxime Ripard Reviewed-by: Simona Vetter Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/2026032451-playing-rummage-8fa2@gregkh --- drivers/gpu/drm/drm_ioc32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index e6b5b06de1487b..f3e40d1e6098b8 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -28,6 +28,7 @@ * IN THE SOFTWARE. */ #include +#include #include #include @@ -374,6 +375,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (nr >= ARRAY_SIZE(drm_compat_ioctls)) return drm_ioctl(filp, cmd, arg); + nr = array_index_nospec(nr, ARRAY_SIZE(drm_compat_ioctls)); fn = drm_compat_ioctls[nr].fn; if (!fn) return drm_ioctl(filp, cmd, arg); From 0179c6da0793ae03607002c284b53b6d584172d0 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 30 Mar 2026 17:02:42 +0200 Subject: [PATCH 351/700] usb: core: phy: avoid double use of 'usb3-phy' Commit 53a2d95df836 ("usb: core: add phy notify connect and disconnect") causes double use of the 'usb3-phy' in certain cases. Since that commit, if a generic PHY named 'usb3-phy' is specified in the device tree, that is getting added to the 'phy_roothub' list of the secondary HCD by the usb_phy_roothub_alloc_usb3_phy() function. However, that PHY is getting added also to the primary HCD's 'phy_roothub' list by usb_phy_roothub_alloc() if there is no generic PHY specified with 'usb2-phy' name. This causes that the usb_add_hcd() function executes each phy operations twice on the 'usb3-phy'. Once when the primary HCD is added, then once again when the secondary HCD is added. The issue affects the Marvell Armada 3700 platform at least, where a custom name is used for the USB2 PHY: $ git grep 'phy-names.*usb3' arch/arm64/boot/dts/marvell/armada-37xx.dtsi | tr '\t' ' ' arch/arm64/boot/dts/marvell/armada-37xx.dtsi: phy-names = "usb3-phy", "usb2-utmi-otg-phy"; Extend the usb_phy_roothub_alloc_usb3_phy() function to skip adding the 'usb3-phy' to the 'phy_roothub' list of the secondary HCD when 'usb2-phy' is not specified in the device tree to avoid the double use. Fixes: 53a2d95df836 ("usb: core: add phy notify connect and disconnect") Cc: stable Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20260330-usb-avoid-usb3-phy-double-use-v1-1-d2113aecb535@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/phy.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index 4bba1c2757406a..4d966cc9cdc951 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc); struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) { struct usb_phy_roothub *phy_roothub; - int num_phys; + int num_phys, usb2_phy_index; if (!IS_ENABLED(CONFIG_GENERIC_PHY)) return NULL; @@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) if (num_phys <= 0) return NULL; + /* + * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added + * all PHYs to the primary HCD's phy_roothub already, so skip + * adding 'usb3-phy' here to avoid double use of that. + */ + usb2_phy_index = of_property_match_string(dev->of_node, "phy-names", + "usb2-phy"); + if (usb2_phy_index < 0) + return NULL; + phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); if (!phy_roothub) return ERR_PTR(-ENOMEM); From 7f6f127b9bc34bed35f56faf7ecb1561d6b39000 Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Tue, 31 Mar 2026 08:04:07 +0800 Subject: [PATCH 352/700] usb: cdns3: gadget: fix NULL pointer dereference in ep_queue When the gadget endpoint is disabled or not yet configured, the ep->desc pointer can be NULL. This leads to a NULL pointer dereference when __cdns3_gadget_ep_queue() is called, causing a kernel crash. Add a check to return -ESHUTDOWN if ep->desc is NULL, which is the standard return code for unconfigured endpoints. This prevents potential crashes when ep_queue is called on endpoints that are not ready. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260331000407.613298-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d59a60a16ec770..96d2a4c38b3f4b 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2589,6 +2589,9 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, struct cdns3_request *priv_req; int ret = 0; + if (!ep->desc) + return -ESHUTDOWN; + request->actual = 0; request->status = -EINPROGRESS; priv_req = to_cdns3_request(request); From 8b7a42ecdcdeb55580d9345412f7f8fc5aca3f6c Mon Sep 17 00:00:00 2001 From: JP Hein Date: Mon, 30 Mar 2026 17:38:04 -0700 Subject: [PATCH 353/700] USB: core: add NO_LPM quirk for Razer Kiyo Pro webcam The Razer Kiyo Pro (1532:0e05) is a USB 3.0 UVC webcam whose firmware does not handle USB Link Power Management transitions reliably. When LPM is active, the device can enter a state where it fails to respond to control transfers, producing EPIPE (-32) errors on UVC probe control SET_CUR requests. In the worst case, the stalled endpoint triggers an xHCI stop-endpoint command that times out, causing the host controller to be declared dead and every USB device on the bus to be disconnected. This has been reported as Ubuntu Launchpad Bug #2061177. The failure mode is: 1. UVC probe control SET_CUR returns -32 (EPIPE) 2. xHCI host not responding to stop endpoint command 3. xHCI host controller not responding, assume dead 4. All USB devices on the affected xHCI controller disconnect Disabling LPM prevents the firmware from entering the problematic low- power states that precede the stall. This is the same approach used for other webcams with similar firmware issues (e.g., Logitech HD Webcam C270). Cc: stable Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2061177 Signed-off-by: JP Hein Link: https://patch.msgid.link/20260331003806.212565-2-jp@jphein.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 26fed25da26edb..0ffdaefba50887 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -493,6 +493,8 @@ static const struct usb_device_id usb_quirk_list[] = { /* Razer - Razer Blade Keyboard */ { USB_DEVICE(0x1532, 0x0116), .driver_info = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* Razer - Razer Kiyo Pro Webcam */ + { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM }, /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */ { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME }, From 33cfe0709b6bf1a7f1a16d5e8d65d003a71b6a21 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 31 Mar 2026 20:05:08 +0800 Subject: [PATCH 354/700] usb: misc: usbio: Fix URB memory leak on submit failure When usb_submit_urb() fails in usbio_probe(), the previously allocated URB is never freed, causing a memory leak. Fix this by jumping to err_free_urb label to properly release the URB on the error path. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: Felix Gu Reviewed-by: Oliver Neukum Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260331-usbio-v2-1-d8c48dad9463@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 2e68d48a2cc075..02d1e0760f0c45 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -614,8 +614,10 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf, usbio->rxbuf_len, usbio_bulk_recv, usbio); ret = usb_submit_urb(usbio->urb, GFP_KERNEL); - if (ret) - return dev_err_probe(dev, ret, "Submitting usb urb\n"); + if (ret) { + dev_err_probe(dev, ret, "Submitting usb urb\n"); + goto err_free_urb; + } mutex_lock(&usbio->ctrl_mutex); @@ -663,6 +665,7 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i err_unlock: mutex_unlock(&usbio->ctrl_mutex); usb_kill_urb(usbio->urb); +err_free_urb: usb_free_urb(usbio->urb); return ret; From 01af542392b5d41fd659d487015a71f627accce3 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 1 Apr 2026 10:51:42 +0800 Subject: [PATCH 355/700] usb: ulpi: fix double free in ulpi_register_interface() error path When device_register() fails, ulpi_register() calls put_device() on ulpi->dev. The device release callback ulpi_dev_release() drops the OF node reference and frees ulpi, but the current error path in ulpi_register_interface() then calls kfree(ulpi) again, causing a double free. Let put_device() handle the cleanup through ulpi_dev_release() and avoid freeing ulpi again in ulpi_register_interface(). Fixes: 289fcff4bcdb1 ("usb: add bus type for USB ULPI") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260401025142.1398996-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 56c9bfaf2ea37e..b34fb65813c45e 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -331,10 +331,9 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ERR_PTR(ret); - } + return ulpi; } From 6e0e34d85cd46ceb37d16054e97a373a32770f6c Mon Sep 17 00:00:00 2001 From: Taegu Ha Date: Thu, 2 Apr 2026 04:13:11 +0900 Subject: [PATCH 356/700] usb: gadget: f_uac1_legacy: validate control request size f_audio_complete() copies req->length bytes into a 4-byte stack variable: u32 data = 0; memcpy(&data, req->buf, req->length); req->length is derived from the host-controlled USB request path, which can lead to a stack out-of-bounds write. Validate req->actual against the expected payload size for the supported control selectors and decode only the expected amount of data. This avoids copying a host-influenced length into a fixed-size stack object. Signed-off-by: Taegu Ha Cc: stable Link: https://patch.msgid.link/20260401191311.3604898-1-hataegu0826@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac1_legacy.c | 47 ++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index a0c953a99727c4..5d201a2e30e7f1 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) { struct f_audio *audio = req->context; - int status = req->status; - u32 data = 0; struct usb_ep *out_ep = audio->out_ep; - switch (status) { - - case 0: /* normal completion? */ - if (ep == out_ep) + switch (req->status) { + case 0: + if (ep == out_ep) { f_audio_out_ep_complete(ep, req); - else if (audio->set_con) { - memcpy(&data, req->buf, req->length); - audio->set_con->set(audio->set_con, audio->set_cmd, - le16_to_cpu(data)); + } else if (audio->set_con) { + struct usb_audio_control *con = audio->set_con; + u8 type = con->type; + u32 data; + bool valid_request = false; + + switch (type) { + case UAC_FU_MUTE: { + u8 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = value; + valid_request = true; + } + break; + } + case UAC_FU_VOLUME: { + __le16 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = le16_to_cpu(value); + valid_request = true; + } + break; + } + } + + if (valid_request) + con->set(con, audio->set_cmd, data); + else + usb_ep_set_halt(ep); + audio->set_con = NULL; } break; From 541288339b8cb8bb62be5ec0eab6c65e9dfc6055 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Wed, 1 Apr 2026 21:49:38 +0800 Subject: [PATCH 357/700] usb: dwc3: imx8mp: fix memory leak on probe failure path When platform_get_drvdata() returns NULL and probe defers, the error path jumps to the 'depopulate' label, skipping put_device() for the reference acquired by of_find_device_by_node(). This extra reference prevents the child platform device from being freed when of_platform_depopulate() is called, resulting in memory leaks reported by kmemleak: unreferenced object 0xffff0000c92c1480 (size 64): comm "kworker/u16:2", pid 50, jiffies 4294895789 backtrace (crc 49d507d0): kmemleak_alloc+0x34/0x40 __kmalloc_noprof+0x430/0x670 of_device_alloc+0xec/0x26c of_platform_device_create_pdata+0x60/0x1f0 of_platform_bus_create+0x290/0x610 of_platform_populate+0x74/0x118 dwc3_imx8mp_probe+0x228/0x734 Fixes: 86767625f525 ("usb: dwc3: imx8mp: disable auto suspend for host role") Signed-off-by: Xiaolei Wang Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260401134938.686748-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-imx8mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index b3d7252bd9104c..1cf96540b66e4e 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -263,7 +263,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev); if (!dwc3) { err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n"); - goto depopulate; + goto put_dwc3; } dwc3->glue_ops = &dwc3_imx_glue_ops; From c32f8748d70c8fc77676ad92ed76cede17bf2c48 Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Wed, 1 Apr 2026 08:10:00 +0800 Subject: [PATCH 358/700] usb: cdns3: gadget: fix state inconsistency on gadget init failure When cdns3_gadget_start() fails, the DRD hardware is left in gadget mode while software state remains INACTIVE, creating hardware/software state inconsistency. When switching to host mode via sysfs: echo host > /sys/class/usb_role/13180000.usb-role-switch/role The role state is not set to CDNS_ROLE_STATE_ACTIVE due to the error, so cdns_role_stop() skips cleanup because state is still INACTIVE. This violates the DRD controller design specification (Figure22), which requires returning to idle state before switching roles. This leads to a synchronous external abort in xhci_gen_setup() when setting up the host controller: [ 516.440698] configfs-gadget 13180000.usb: failed to start g1: -19 [ 516.442035] cdns-usb3 13180000.usb: Failed to add gadget [ 516.443278] cdns-usb3 13180000.usb: set role 2 has failed ... [ 1301.375722] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller [ 1301.377716] Internal error: synchronous external abort: 96000010 [#1] PREEMPT SMP [ 1301.382485] pc : xhci_gen_setup+0xa4/0x408 [ 1301.393391] backtrace: ... xhci_gen_setup+0xa4/0x408 <-- CRASH xhci_plat_setup+0x44/0x58 usb_add_hcd+0x284/0x678 ... cdns_role_set+0x9c/0xbc <-- Role switch Fix by calling cdns_drd_gadget_off() in the error path to properly clean up the DRD gadget state. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260401001000.5761-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 96d2a4c38b3f4b..8382231af35749 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3431,6 +3431,7 @@ static int __cdns3_gadget_init(struct cdns *cdns) ret = cdns3_gadget_start(cdns); if (ret) { pm_runtime_put_sync(cdns->dev); + cdns_drd_gadget_off(cdns); return ret; } From bd3d245b0fef571f93504904df62b8865b1c0d34 Mon Sep 17 00:00:00 2001 From: Guan-Yu Lin Date: Wed, 1 Apr 2026 12:32:17 +0000 Subject: [PATCH 359/700] usb: core: use dedicated spinlock for offload state Replace the coarse USB device lock with a dedicated offload_lock spinlock to reduce contention during offload operations. Use offload_pm_locked to synchronize with PM transitions and replace the legacy offload_at_suspend flag. Optimize usb_offload_get/put by switching from auto-resume/suspend to pm_runtime_get_if_active(). This ensures offload state is only modified when the device is already active, avoiding unnecessary power transitions. Cc: stable Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage") Signed-off-by: Guan-Yu Lin Tested-by: Hailong Liu Acked-by: Mathias Nyman Link: https://patch.msgid.link/20260401123238.3790062-2-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 23 ++++--- drivers/usb/core/offload.c | 102 ++++++++++++++++++------------- drivers/usb/core/usb.c | 1 + drivers/usb/host/xhci-sideband.c | 4 +- include/linux/usb.h | 10 ++- 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2574e65bc640b7..f63004417058e6 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1415,14 +1415,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i = 0, n = 0; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) goto done; + usb_offload_set_pm_locked(udev, true); if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) { dev_dbg(&udev->dev, "device offloaded, skip suspend.\n"); - udev->offload_at_suspend = 1; + offload_active = true; } /* Suspend all the interfaces and then udev itself */ @@ -1436,8 +1438,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * interrupt urbs, allowing interrupt events to be * handled during system suspend. */ - if (udev->offload_at_suspend && - intf->needs_remote_wakeup) { + if (offload_active && intf->needs_remote_wakeup) { dev_dbg(&intf->dev, "device offloaded, skip suspend.\n"); continue; @@ -1452,7 +1453,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } } if (status == 0) { - if (!udev->offload_at_suspend) + if (!offload_active) status = usb_suspend_device(udev, msg); /* @@ -1498,7 +1499,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) */ } else { udev->can_submit = 0; - if (!udev->offload_at_suspend) { + if (!offload_active) { for (i = 0; i < 16; ++i) { usb_hcd_flush_endpoint(udev, udev->ep_out[i]); usb_hcd_flush_endpoint(udev, udev->ep_in[i]); @@ -1507,6 +1508,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } done: + if (status != 0) + usb_offload_set_pm_locked(udev, false); dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); return status; } @@ -1536,16 +1539,19 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED) { status = -ENODEV; goto done; } udev->can_submit = 1; + if (msg.event == PM_EVENT_RESUME) + offload_active = usb_offload_check(udev); /* Resume the device */ if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) { - if (!udev->offload_at_suspend) + if (!offload_active) status = usb_resume_device(udev, msg); else dev_dbg(&udev->dev, @@ -1562,8 +1568,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) * pending interrupt urbs, allowing interrupt events * to be handled during system suspend. */ - if (udev->offload_at_suspend && - intf->needs_remote_wakeup) { + if (offload_active && intf->needs_remote_wakeup) { dev_dbg(&intf->dev, "device offloaded, skip resume.\n"); continue; @@ -1572,11 +1577,11 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) udev->reset_resume); } } - udev->offload_at_suspend = 0; usb_mark_last_busy(udev); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); + usb_offload_set_pm_locked(udev, false); if (!status) udev->reset_resume = 0; return status; diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c index 7c699f1b8d2b78..9db3cfedd29c7b 100644 --- a/drivers/usb/core/offload.c +++ b/drivers/usb/core/offload.c @@ -25,33 +25,30 @@ */ int usb_offload_get(struct usb_device *udev) { - int ret; + int ret = 0; - usb_lock_device(udev); - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(udev); + if (!usb_get_dev(udev)) return -ENODEV; - } - if (udev->state == USB_STATE_SUSPENDED || - udev->offload_at_suspend) { - usb_unlock_device(udev); - return -EBUSY; + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; } - /* - * offload_usage could only be modified when the device is active, since - * it will alter the suspend flow of the device. - */ - ret = usb_autoresume_device(udev); - if (ret < 0) { - usb_unlock_device(udev); - return ret; + spin_lock(&udev->offload_lock); + + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; } udev->offload_usage++; - usb_autosuspend_device(udev); - usb_unlock_device(udev); + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); return ret; } @@ -69,35 +66,32 @@ EXPORT_SYMBOL_GPL(usb_offload_get); */ int usb_offload_put(struct usb_device *udev) { - int ret; + int ret = 0; - usb_lock_device(udev); - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(udev); + if (!usb_get_dev(udev)) return -ENODEV; - } - if (udev->state == USB_STATE_SUSPENDED || - udev->offload_at_suspend) { - usb_unlock_device(udev); - return -EBUSY; + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; } - /* - * offload_usage could only be modified when the device is active, since - * it will alter the suspend flow of the device. - */ - ret = usb_autoresume_device(udev); - if (ret < 0) { - usb_unlock_device(udev); - return ret; + spin_lock(&udev->offload_lock); + + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; } /* Drop the count when it wasn't 0, ignore the operation otherwise. */ if (udev->offload_usage) udev->offload_usage--; - usb_autosuspend_device(udev); - usb_unlock_device(udev); + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); return ret; } @@ -112,25 +106,47 @@ EXPORT_SYMBOL_GPL(usb_offload_put); * management. * * The caller must hold @udev's device lock. In addition, the caller should - * ensure downstream usb devices are all either suspended or marked as - * "offload_at_suspend" to ensure the correctness of the return value. + * ensure the device itself and the downstream usb devices are all marked as + * "offload_pm_locked" to ensure the correctness of the return value. * * Returns true on any offload activity, false otherwise. */ bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex) { struct usb_device *child; - bool active; + bool active = false; int port1; + if (udev->offload_usage) + return true; + usb_hub_for_each_child(udev, port1, child) { usb_lock_device(child); active = usb_offload_check(child); usb_unlock_device(child); + if (active) - return true; + break; } - return !!udev->offload_usage; + return active; } EXPORT_SYMBOL_GPL(usb_offload_check); + +/** + * usb_offload_set_pm_locked - set the PM lock state of a USB device + * @udev: the USB device to modify + * @locked: the new lock state + * + * Setting @locked to true prevents offload_usage from being modified. This + * ensures that offload activities cannot be started or stopped during critical + * power management transitions, maintaining a stable state for the duration + * of the transition. + */ +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) +{ + spin_lock(&udev->offload_lock); + udev->offload_pm_locked = locked; + spin_unlock(&udev->offload_lock); +} +EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e9a10a33534ce0..df166cafe10619 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -671,6 +671,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, set_dev_node(&dev->dev, dev_to_node(bus->sysdev)); dev->state = USB_STATE_ATTACHED; dev->lpm_disable_count = 1; + spin_lock_init(&dev->offload_lock); dev->offload_usage = 0; atomic_set(&dev->urbnum, 0); diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index abbcc0e44f1b8e..54284d29d2016c 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -291,8 +291,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer); * Allow other drivers, such as usb controller driver, to check if there are * any sideband activity on the host controller. This information could be used * for power management or other forms of resource management. The caller should - * ensure downstream usb devices are all either suspended or marked as - * "offload_at_suspend" to ensure the correctness of the return value. + * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure + * the correctness of the return value. * * Returns true on any active sideband existence, false otherwise. */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 04277af4bb9d56..4aab200158517e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -21,6 +21,7 @@ #include /* for struct completion */ #include /* for current && schedule_timeout */ #include /* for struct mutex */ +#include /* for spinlock_t */ #include /* for runtime PM */ struct usb_device; @@ -636,8 +637,9 @@ struct usb3_lpm_parameters { * @do_remote_wakeup: remote wakeup should be enabled * @reset_resume: needs reset instead of resume * @port_is_suspended: the upstream port is suspended (L2 or U3) - * @offload_at_suspend: offload activities during suspend is enabled. + * @offload_pm_locked: prevents offload_usage changes during PM transitions. * @offload_usage: number of offload activities happening on this usb device. + * @offload_lock: protects offload_usage and offload_pm_locked * @slot_id: Slot ID assigned by xHCI * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout. * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. @@ -726,8 +728,9 @@ struct usb_device { unsigned do_remote_wakeup:1; unsigned reset_resume:1; unsigned port_is_suspended:1; - unsigned offload_at_suspend:1; + unsigned offload_pm_locked:1; int offload_usage; + spinlock_t offload_lock; enum usb_link_tunnel_mode tunnel_mode; struct device_link *usb4_link; @@ -849,6 +852,7 @@ static inline void usb_mark_last_busy(struct usb_device *udev) int usb_offload_get(struct usb_device *udev); int usb_offload_put(struct usb_device *udev); bool usb_offload_check(struct usb_device *udev); +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked); #else static inline int usb_offload_get(struct usb_device *udev) @@ -857,6 +861,8 @@ static inline int usb_offload_put(struct usb_device *udev) { return 0; } static inline bool usb_offload_check(struct usb_device *udev) { return false; } +static inline void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) +{ } #endif extern int usb_disable_lpm(struct usb_device *udev); From 5abbe6ecc6203355c770bf232ade88e29c960049 Mon Sep 17 00:00:00 2001 From: Guan-Yu Lin Date: Wed, 1 Apr 2026 12:32:18 +0000 Subject: [PATCH 360/700] usb: host: xhci-sideband: delegate offload_usage tracking to class drivers Remove usb_offload_get() and usb_offload_put() from the xHCI sideband interrupter creation and removal paths. The responsibility of manipulating offload_usage now lies entirely with the USB class drivers. They have the precise context of when an offload data stream actually starts and stops, ensuring a much more accurate representation of offload activity for power management. Cc: stable Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage") Signed-off-by: Guan-Yu Lin Tested-by: Hailong Liu Tested-by: hailong.liu@oppo.com Acked-by: Mathias Nyman Link: https://patch.msgid.link/20260401123238.3790062-3-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-sideband.c | 14 +------------- sound/usb/qcom/qc_audio_offload.c | 10 +++++++++- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 54284d29d2016c..23153e136d4b84 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e static void __xhci_sideband_remove_interrupter(struct xhci_sideband *sb) { - struct usb_device *udev; - lockdep_assert_held(&sb->mutex); if (!sb->ir) @@ -102,10 +100,6 @@ __xhci_sideband_remove_interrupter(struct xhci_sideband *sb) xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir); sb->ir = NULL; - udev = sb->vdev->udev; - - if (udev->state != USB_STATE_NOTATTACHED) - usb_offload_put(udev); } /* sideband api functions */ @@ -328,9 +322,6 @@ int xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, bool ip_autoclear, u32 imod_interval, int intr_num) { - int ret = 0; - struct usb_device *udev; - if (!sb || !sb->xhci) return -ENODEV; @@ -348,12 +339,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, if (!sb->ir) return -ENOMEM; - udev = sb->vdev->udev; - ret = usb_offload_get(udev); - sb->ir->ip_autoclear = ip_autoclear; - return ret; + return 0; } EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter); diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 510b68cced33c1..a8d313fa254ac2 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -699,6 +699,7 @@ static void uaudio_event_ring_cleanup_free(struct uaudio_dev *dev) uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE); xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb); + usb_offload_put(dev->udev); } } @@ -1182,12 +1183,16 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev); er_pa = 0; + ret = usb_offload_get(subs->dev); + if (ret < 0) + goto exit; + /* event ring */ ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false, 0, uaudio_qdev->data->intr_num); if (ret < 0) { dev_err(&subs->dev->dev, "failed to fetch interrupter\n"); - goto exit; + goto put_offload; } sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb); @@ -1219,6 +1224,8 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, mem_info->dma = 0; remove_interrupter: xhci_sideband_remove_interrupter(uadev[card_num].sb); +put_offload: + usb_offload_put(subs->dev); exit: return ret; } @@ -1482,6 +1489,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE); free_sec_ring: xhci_sideband_remove_interrupter(uadev[card_num].sb); + usb_offload_put(subs->dev); drop_sync_ep: if (subs->sync_endpoint) { uaudio_iommu_unmap(MEM_XFER_RING, From 4e0a88254ad59f6c53a34bf5fa241884ec09e8b2 Mon Sep 17 00:00:00 2001 From: Michael Zimmermann Date: Tue, 31 Mar 2026 20:48:44 +0200 Subject: [PATCH 361/700] usb: gadget: f_hid: move list and spinlock inits from bind to alloc There was an issue when you did the following: - setup and bind an hid gadget - open /dev/hidg0 - use the resulting fd in EPOLL_CTL_ADD - unbind the UDC - bind the UDC - use the fd in EPOLL_CTL_DEL When CONFIG_DEBUG_LIST was enabled, a list_del corruption was reported within remove_wait_queue (via ep_remove_wait_queue). After some debugging I found out that the queues, which f_hid registers via poll_wait were the problem. These were initialized using init_waitqueue_head inside hidg_bind. So effectively, the bind function re-initialized the queues while there were still items in them. The solution is to move the initialization from hidg_bind to hidg_alloc to extend their lifetimes to the lifetime of the function instance. Additionally, I found many other possibly problematic init calls in the bind function, which I moved as well. Signed-off-by: Michael Zimmermann Cc: stable Link: https://patch.msgid.link/20260331184844.2388761-1-sigmaepsilon92@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_hid.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 8812ebf33d14bc..e5ccaec7750cdc 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1262,17 +1262,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - spin_lock_init(&hidg->write_spinlock); hidg->write_pending = 1; hidg->req = NULL; - spin_lock_init(&hidg->read_spinlock); - spin_lock_init(&hidg->get_report_spinlock); - init_waitqueue_head(&hidg->write_queue); - init_waitqueue_head(&hidg->read_queue); - init_waitqueue_head(&hidg->get_queue); - init_waitqueue_head(&hidg->get_id_queue); - INIT_LIST_HEAD(&hidg->completed_out_req); - INIT_LIST_HEAD(&hidg->report_list); INIT_WORK(&hidg->work, get_report_workqueue_handler); hidg->workqueue = alloc_workqueue("report_work", @@ -1608,6 +1599,16 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) mutex_lock(&opts->lock); + spin_lock_init(&hidg->write_spinlock); + spin_lock_init(&hidg->read_spinlock); + spin_lock_init(&hidg->get_report_spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + init_waitqueue_head(&hidg->get_queue); + init_waitqueue_head(&hidg->get_id_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + INIT_LIST_HEAD(&hidg->report_list); + device_initialize(&hidg->dev); hidg->dev.release = hidg_release; hidg->dev.class = &hidg_class; From cf6c18cf83e48986ac40a053d09d3c33624135f6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Apr 2026 17:57:34 +0100 Subject: [PATCH 362/700] ALSA: compress: Refuse to update timestamps for unconfigured streams There are a number of mechanisms, including the userspace accessible timestamp and buffer availability ioctl()s, which allow us to trigger a timestamp update on a stream before it has been configured. Since drivers might rely on stream configuration for reporting of pcm_io_frames, including potentially doing a division by the number of channels, and these operations are not meaningful for an unconfigured stream reject attempts to read timestamps before any configuration is done. Signed-off-by: Mark Brown Acked-by: Vinod Koul Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260401-alsa-unconfigured-tstamp-v1-1-694c2cb5f71d@kernel.org --- sound/core/compress_offload.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 5a0308eb4e31de..db9f516df8422c 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -185,6 +185,14 @@ static int snd_compr_update_tstamp(struct snd_compr_stream *stream, { if (!stream->ops->pointer) return -ENOTSUPP; + + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + default: + break; + } + stream->ops->pointer(stream, tstamp); pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset, tstamp->copied_total); From 61327f3d817cb5820559ad4f8d0d9abed3d379b1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Apr 2026 17:57:35 +0100 Subject: [PATCH 363/700] ALSA: compress: Pay attention if drivers error out retrieving pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we have a return code on the driver pointer operation but the core ignores that. Let's start paying attention. Reported-by: Péter Ujfalusi Signed-off-by: Mark Brown Acked-by: Vinod Koul Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260401-alsa-unconfigured-tstamp-v1-2-694c2cb5f71d@kernel.org --- sound/core/compress_offload.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index db9f516df8422c..fd63d219bf8665 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -183,6 +183,8 @@ snd_compr_tstamp32_from_64(struct snd_compr_tstamp *tstamp32, static int snd_compr_update_tstamp(struct snd_compr_stream *stream, struct snd_compr_tstamp64 *tstamp) { + int ret; + if (!stream->ops->pointer) return -ENOTSUPP; @@ -193,7 +195,9 @@ static int snd_compr_update_tstamp(struct snd_compr_stream *stream, break; } - stream->ops->pointer(stream, tstamp); + ret = stream->ops->pointer(stream, tstamp); + if (ret != 0) + return ret; pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset, tstamp->copied_total); if (stream->direction == SND_COMPRESS_PLAYBACK) From 9e07e3b81807edd356e1f794cffa00a428eff443 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 1 Apr 2026 16:33:53 +0200 Subject: [PATCH 364/700] thermal: core: Fix thermal zone device registration error path If thermal_zone_device_register_with_trips() fails after registering a thermal zone device, it needs to wait for the tz->removal completion like thermal_zone_device_unregister(), in case user space has managed to take a reference to the thermal zone device's kobject, in which case thermal_release() may not be called by the error path itself and tz may be freed prematurely. Add the missing wait_for_completion() call to the thermal zone device registration error path. Fixes: 04e6ccfc93c5 ("thermal: core: Fix NULL pointer dereference in zone registration error path") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba Link: https://patch.msgid.link/2849815.mvXUDI8C0e@rafael.j.wysocki --- drivers/thermal/thermal_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 5337612e484da6..d1beee9e15f8ef 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1642,6 +1642,7 @@ thermal_zone_device_register_with_trips(const char *type, device_del(&tz->device); release_device: put_device(&tz->device); + wait_for_completion(&tz->removal); remove_id: ida_free(&thermal_tz_ida, id); free_tzp: From 1558905669e4da922fbaa7cf6507eb14779bffbd Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Thu, 2 Apr 2026 10:36:04 +0800 Subject: [PATCH 365/700] ALSA: aoa/tas: Fix OF node leak on probe failure Add missing of_node_put() in the error path. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260402023604.54682-1-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/aoa/codecs/tas.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index 13da2b159ad0db..25214d3da65d1d 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -872,6 +872,7 @@ static int tas_i2c_probe(struct i2c_client *client) return 0; fail: mutex_destroy(&tas->mtx); + of_node_put(tas->codec.node); kfree(tas); return -EINVAL; } From 4513d3e0bbc0585b86ccf2631902593ff97e88f5 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Thu, 2 Apr 2026 13:36:57 +0800 Subject: [PATCH 366/700] ALSA: usb-audio: apply quirk for MOONDROP JU Jiu It(ID 31b2:0111 JU Jiu) reports a MIN value -12800 for volume control, but will mute when setting it less than -10880. Thanks to my girlfriend Kagura for reporting this issue. Cc: Kagura Cc: stable@vger.kernel.org Signed-off-by: Cryolitia PukNgae Link: https://patch.msgid.link/20260402-syy-v1-1-068d3bc30ddc@linux.dev Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 69026cf54979a8..a25e8145af67e5 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1204,6 +1204,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->min = -11264; /* Mute under it */ } break; + case USB_ID(0x31b2, 0x0111): /* MOONDROP JU Jiu */ + if (!strcmp(kctl->id.name, "PCM Playback Volume")) { + usb_audio_info(chip, + "set volume quirk for MOONDROP JU Jiu\n"); + cval->min = -10880; /* Mute under it */ + } + break; } } From aaefbdde9abdc43699e110679c0e10972a5e1c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 30 Mar 2026 14:51:43 -0300 Subject: [PATCH 367/700] drm/vc4: Release runtime PM reference after binding V3D MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vc4_v3d_bind() function acquires a runtime PM reference via pm_runtime_resume_and_get() to access V3D registers during setup. However, this reference is never released after a successful bind. This prevents the device from ever runtime suspending, since the reference count never reaches zero. Release the runtime PM reference by adding pm_runtime_put_autosuspend() after autosuspend is configured, allowing the device to runtime suspend after the delay. Fixes: 266cff37d7fc ("drm/vc4: v3d: Rework the runtime_pm setup") Reviewed-by: Melissa Wen Link: https://patch.msgid.link/20260330-vc4-misc-fixes-v1-1-92defc940a29@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_v3d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 3ffe09bc89d273..d31b906cb8e787 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -481,6 +481,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */ + pm_runtime_put_autosuspend(dev); return 0; From f4dfd6847b3e5d24e336bca6057485116d17aea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 30 Mar 2026 14:51:44 -0300 Subject: [PATCH 368/700] drm/vc4: Fix memory leak of BO array in hang state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hang state's BO array is allocated separately with kzalloc() in vc4_save_hang_state() but never freed in vc4_free_hang_state(). Add the missing kfree() for the BO array before freeing the hang state struct. Fixes: 214613656b51 ("drm/vc4: Add an interface for capturing the GPU state after a hang.") Reviewed-by: Melissa Wen Link: https://patch.msgid.link/20260330-vc4-misc-fixes-v1-2-92defc940a29@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index ad8cbd727b801c..a64d5a96245283 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -62,6 +62,7 @@ vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) for (i = 0; i < state->user_state.bo_count; i++) drm_gem_object_put(state->bo[i]); + kfree(state->bo); kfree(state); } From 9525d169e5fd481538cf8c663cc5839e54f2e481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 30 Mar 2026 14:51:45 -0300 Subject: [PATCH 369/700] drm/vc4: Fix a memory leak in hang state error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When vc4_save_hang_state() encounters an early return condition, it returns without freeing the previously allocated `kernel_state`, leaking memory. Add the missing kfree() calls by consolidating the early return paths into a single place. Fixes: 214613656b51 ("drm/vc4: Add an interface for capturing the GPU state after a hang.") Reviewed-by: Melissa Wen Link: https://patch.msgid.link/20260330-vc4-misc-fixes-v1-3-92defc940a29@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_gem.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index a64d5a96245283..34bdac107ce18f 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -171,10 +171,8 @@ vc4_save_hang_state(struct drm_device *dev) spin_lock_irqsave(&vc4->job_lock, irqflags); exec[0] = vc4_first_bin_job(vc4); exec[1] = vc4_first_render_job(vc4); - if (!exec[0] && !exec[1]) { - spin_unlock_irqrestore(&vc4->job_lock, irqflags); - return; - } + if (!exec[0] && !exec[1]) + goto err_free_state; /* Get the bos from both binner and renderer into hang state. */ state->bo_count = 0; @@ -191,10 +189,8 @@ vc4_save_hang_state(struct drm_device *dev) kernel_state->bo = kzalloc_objs(*kernel_state->bo, state->bo_count, GFP_ATOMIC); - if (!kernel_state->bo) { - spin_unlock_irqrestore(&vc4->job_lock, irqflags); - return; - } + if (!kernel_state->bo) + goto err_free_state; k = 0; for (i = 0; i < 2; i++) { @@ -286,6 +282,12 @@ vc4_save_hang_state(struct drm_device *dev) vc4->hang_state = kernel_state; spin_unlock_irqrestore(&vc4->job_lock, irqflags); } + + return; + +err_free_state: + spin_unlock_irqrestore(&vc4->job_lock, irqflags); + kfree(kernel_state); } static void From 338c56050d8e892604da97f67bfa8cc4015a955f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 30 Mar 2026 14:51:46 -0300 Subject: [PATCH 370/700] drm/vc4: Protect madv read in vc4_gem_object_mmap() with madv_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mmap callback reads bo->madv without holding madv_lock, racing with concurrent DRM_IOCTL_VC4_GEM_MADVISE calls that modify the field under the same lock. Add the missing locking to prevent the data race. Fixes: b9f19259b84d ("drm/vc4: Add the DRM_IOCTL_VC4_GEM_MADVISE ioctl") Reviewed-by: Melissa Wen Link: https://patch.msgid.link/20260330-vc4-misc-fixes-v1-4-92defc940a29@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_bo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 1f93bc5a3d02eb..cb2b62a4197207 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -738,12 +738,15 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct return -EINVAL; } + mutex_lock(&bo->madv_lock); if (bo->madv != VC4_MADV_WILLNEED) { DRM_DEBUG("mmapping of %s BO not allowed\n", bo->madv == VC4_MADV_DONTNEED ? "purgeable" : "purged"); + mutex_unlock(&bo->madv_lock); return -EINVAL; } + mutex_unlock(&bo->madv_lock); return drm_gem_dma_mmap(&bo->base, vma); } From d10a26aa4d072320530e6968ef945c8c575edf61 Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Tue, 31 Mar 2026 09:43:17 +0200 Subject: [PATCH 371/700] net/x25: Fix potential double free of skb When alloc_skb fails in x25_queue_rx_frame it calls kfree_skb(skb) at line 48 and returns 1 (error). This error propagates back through the call chain: x25_queue_rx_frame returns 1 | v x25_state3_machine receives the return value 1 and takes the else branch at line 278, setting queued=0 and returning 0 | v x25_process_rx_frame returns queued=0 | v x25_backlog_rcv at line 452 sees queued=0 and calls kfree_skb(skb) again This would free the same skb twice. Looking at x25_backlog_rcv: net/x25/x25_in.c:x25_backlog_rcv() { ... queued = x25_process_rx_frame(sk, skb); ... if (!queued) kfree_skb(skb); } Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Martin Schiller Link: https://patch.msgid.link/20260331-x25_fraglen-v4-1-3e69f18464b4@dev.tdt.de Signed-off-by: Paolo Abeni --- net/x25/x25_in.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index b981a4828d08c2..0dbc73efab1cbd 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -44,10 +44,9 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) if (x25->fraglen > 0) { /* End of fragment */ int len = x25->fraglen + skb->len; - if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){ - kfree_skb(skb); + skbn = alloc_skb(len, GFP_ATOMIC); + if (!skbn) return 1; - } skb_queue_tail(&x25->fragment_queue, skb); From a1822cb524e89b4cd2cf0b82e484a2335496a6d9 Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Tue, 31 Mar 2026 09:43:18 +0200 Subject: [PATCH 372/700] net/x25: Fix overflow when accumulating packets Add a check to ensure that `x25_sock.fraglen` does not overflow. The `fraglen` also needs to be resetted when purging `fragment_queue` in `x25_clear_queues()`. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Suggested-by: Yiming Qian Signed-off-by: Martin Schiller Link: https://patch.msgid.link/20260331-x25_fraglen-v4-2-3e69f18464b4@dev.tdt.de Signed-off-by: Paolo Abeni --- net/x25/x25_in.c | 4 ++++ net/x25/x25_subr.c | 1 + 2 files changed, 5 insertions(+) diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 0dbc73efab1cbd..e47ebd8acd21bf 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -34,6 +34,10 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) struct sk_buff *skbo, *skbn = skb; struct x25_sock *x25 = x25_sk(sk); + /* make sure we don't overflow */ + if (x25->fraglen + skb->len > USHRT_MAX) + return 1; + if (more) { x25->fraglen += skb->len; skb_queue_tail(&x25->fragment_queue, skb); diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 0285aaa1e93c17..159708d9ad20cb 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c @@ -40,6 +40,7 @@ void x25_clear_queues(struct sock *sk) skb_queue_purge(&x25->interrupt_in_queue); skb_queue_purge(&x25->interrupt_out_queue); skb_queue_purge(&x25->fragment_queue); + x25->fraglen = 0; } From 1319ea57529e131822bab56bf417c8edc2db9ae8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 1 Apr 2026 15:20:20 +0200 Subject: [PATCH 373/700] sched/fair: Fix zero_vruntime tracking fix John reported that stress-ng-yield could make his machine unhappy and managed to bisect it to commit b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking"). The combination of yield and that commit was specific enough to hypothesize the following scenario: Suppose we have 2 runnable tasks, both doing yield. Then one will be eligible and one will not be, because the average position must be in between these two entities. Therefore, the runnable task will be eligible, and be promoted a full slice (all the tasks do is yield after all). This causes it to jump over the other task and now the other task is eligible and current is no longer. So we schedule. Since we are runnable, there is no {de,en}queue. All we have is the __{en,de}queue_entity() from {put_prev,set_next}_task(). But per the fingered commit, those two no longer move zero_vruntime. All that moves zero_vruntime are tick and full {de,en}queue. This means, that if the two tasks playing leapfrog can reach the critical speed to reach the overflow point inside one tick's worth of time, we're up a creek. Additionally, when multiple cgroups are involved, there is no guarantee the tick will in fact hit every cgroup in a timely manner. Statistically speaking it will, but that same statistics does not rule out the possibility of one cgroup not getting a tick for a significant amount of time -- however unlikely. Therefore, just like with the yield() case, force an update at the end of every slice. This ensures the update is never more than a single slice behind and the whole thing is within 2 lag bounds as per the comment on entity_key(). Fixes: b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking") Reported-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Tested-by: K Prateek Nayak Tested-by: John Stultz Link: https://patch.msgid.link/20260401132355.081530332@infradead.org --- kernel/sched/fair.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bf948db905ed1b..ab4114712be740 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -707,7 +707,7 @@ void update_zero_vruntime(struct cfs_rq *cfs_rq, s64 delta) * Called in: * - place_entity() -- before enqueue * - update_entity_lag() -- before dequeue - * - entity_tick() + * - update_deadline() -- slice expiration * * This means it is one entry 'behind' but that puts it close enough to where * the bound on entity_key() is at most two lag bounds. @@ -1131,6 +1131,7 @@ static bool update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) * EEVDF: vd_i = ve_i + r_i / w_i */ se->deadline = se->vruntime + calc_delta_fair(se->slice, se); + avg_vruntime(cfs_rq); /* * The task has consumed its request, reschedule. @@ -5593,11 +5594,6 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) update_load_avg(cfs_rq, curr, UPDATE_TG); update_cfs_group(curr); - /* - * Pulls along cfs_rq::zero_vruntime. - */ - avg_vruntime(cfs_rq); - #ifdef CONFIG_SCHED_HRTICK /* * queued ticks are scheduled to match the slice, so don't bother @@ -9128,7 +9124,7 @@ static void yield_task_fair(struct rq *rq) */ if (entity_eligible(cfs_rq, se)) { se->vruntime = se->deadline; - se->deadline += calc_delta_fair(se->slice, se); + update_deadline(cfs_rq, se); } } From e08d007f9d813616ce7093600bc4fdb9c9d81d89 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 1 Apr 2026 15:20:21 +0200 Subject: [PATCH 374/700] sched/debug: Fix avg_vruntime() usage John reported that stress-ng-yield could make his machine unhappy and managed to bisect it to commit b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking"). The commit in question changes avg_vruntime() from a function that is a pure reader, to a function that updates variables. This turns an unlocked sched/debug usage of this function from a minor mistake into a data corruptor. Fixes: af4cf40470c2 ("sched/fair: Add cfs_rq::avg_vruntime") Fixes: b3d99f43c72b ("sched/fair: Fix zero_vruntime tracking") Reported-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Tested-by: K Prateek Nayak Tested-by: John Stultz Link: https://patch.msgid.link/20260401132355.196370805@infradead.org --- kernel/sched/debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index b24f40f05019a8..15bf45b6f91241 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -902,6 +902,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu) void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) { s64 left_vruntime = -1, zero_vruntime, right_vruntime = -1, left_deadline = -1, spread; + u64 avruntime; struct sched_entity *last, *first, *root; struct rq *rq = cpu_rq(cpu); unsigned long flags; @@ -925,6 +926,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) if (last) right_vruntime = last->vruntime; zero_vruntime = cfs_rq->zero_vruntime; + avruntime = avg_vruntime(cfs_rq); raw_spin_rq_unlock_irqrestore(rq, flags); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "left_deadline", @@ -934,7 +936,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "zero_vruntime", SPLIT_NS(zero_vruntime)); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "avg_vruntime", - SPLIT_NS(avg_vruntime(cfs_rq))); + SPLIT_NS(avruntime)); SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "right_vruntime", SPLIT_NS(right_vruntime)); spread = right_vruntime - left_vruntime; From dbde07f06226438cd2cf1179745fa1bec5d8914a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 12 Mar 2026 12:43:05 -0700 Subject: [PATCH 375/700] perf/x86: Fix potential bad container_of in intel_pmu_hw_config Auto counter reload may have a group of events with software events present within it. The software event PMU isn't the x86_hybrid_pmu and a container_of operation in intel_pmu_set_acr_caused_constr (via the hybrid helper) could cause out of bound memory reads. Avoid this by guarding the call to intel_pmu_set_acr_caused_constr with an is_x86_event check. Fixes: ec980e4facef ("perf/x86/intel: Support auto counter reload") Signed-off-by: Ian Rogers Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Thomas Falcon Link: https://patch.msgid.link/20260312194305.1834035-1-irogers@google.com --- arch/x86/events/intel/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 36c68210d4d2fe..793335c3ce787d 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -4855,8 +4855,10 @@ static int intel_pmu_hw_config(struct perf_event *event) intel_pmu_set_acr_caused_constr(leader, idx++, cause_mask); if (leader->nr_siblings) { - for_each_sibling_event(sibling, leader) - intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask); + for_each_sibling_event(sibling, leader) { + if (is_x86_event(sibling)) + intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask); + } } if (leader != event) From 579af7204d762587f9cce0d6236a710a771f1f6f Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Mon, 2 Feb 2026 10:47:55 +0100 Subject: [PATCH 376/700] gpib: Fix fluke driver s390 compile issue The following errors were reported for a s390 randconfig build of the fluke gpib driver: >> drivers/gpib/eastwood/fluke_gpib.c:1002:23: error: call to undeclared function 'ioremap'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 1002 | nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, | ^ >> drivers/gpib/eastwood/fluke_gpib.c:1002:21: error: incompatible integer to pointer conversion assigning to 'void *' from 'int' [-Wint-conversion] 1002 | nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start, | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1003 | resource_size(e_priv->gpib_iomem_res)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/gpib/eastwood/fluke_gpib.c:1036:33: error: incompatible integer to pointer conversion assigning to 'void *' from 'int' [-Wint-conversion] 1036 | e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start, | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1037 | resource_size(e_priv->write_transfer_counter_res)); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add HAS_IOMEM dependency to Kconfig for fluke driver option Suggested-by: Arnd Bergmann Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601221748.AFAqHieJ-lkp@intel.com/ Fixes: baf8855c9160 ("staging: gpib: fix address space mixup") Cc: stable Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260202094755.4259-1-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpib/Kconfig b/drivers/gpib/Kconfig index eeb50956ce85c6..d43a28c62ed7b4 100644 --- a/drivers/gpib/Kconfig +++ b/drivers/gpib/Kconfig @@ -122,6 +122,7 @@ config GPIB_FLUKE depends on OF select GPIB_COMMON select GPIB_NEC7210 + depends on HAS_IOMEM help GPIB driver for Fluke based cda devices. From 5cefb52c1af6f69ea719e42788f6ec6a087eb74c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Mar 2026 11:51:27 +0100 Subject: [PATCH 377/700] gpib: lpvo_usb: fix memory leak on disconnect The driver iterates over the registered USB interfaces during GPIB attach and takes a reference to their USB devices until a match is found. These references are never released which leads to a memory leak when devices are disconnected. Fix the leak by dropping the unnecessary references. Fixes: fce79512a96a ("staging: gpib: Add LPVO DIY USB GPIB driver") Cc: stable # 6.13 Cc: Dave Penkler Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260310105127.17538-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index ee781d2f0b8e1d..0f9d385bc50b52 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -406,7 +406,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con for (j = 0 ; j < MAX_DEV ; j++) { if ((assigned_usb_minors & 1 << j) == 0) continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + udev = interface_to_usbdev(lpvo_usb_interfaces[j]); device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL); match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev, config->device_path); @@ -421,7 +421,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con for (j = 0 ; j < MAX_DEV ; j++) { if ((assigned_usb_minors & 1 << j) == 0) continue; - udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j])); + udev = interface_to_usbdev(lpvo_usb_interfaces[j]); DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j, udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot); if (config->pci_bus == udev->bus->busnum && From d1857f8296dceb75d00ab857fc3c61bc00c7f5c6 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Tue, 17 Mar 2026 19:25:28 +0700 Subject: [PATCH 378/700] gpib: fix use-after-free in IO ioctl handlers The IBRD, IBWRT, IBCMD, and IBWAIT ioctl handlers use a gpib_descriptor pointer after board->big_gpib_mutex has been released. A concurrent IBCLOSEDEV ioctl can free the descriptor via close_dev_ioctl() during this window, causing a use-after-free. The IO handlers (read_ioctl, write_ioctl, command_ioctl) explicitly release big_gpib_mutex before calling their handler. wait_ioctl() is called with big_gpib_mutex held, but ibwait() releases it internally when wait_mask is non-zero. In all four cases, the descriptor pointer obtained from handle_to_descriptor() becomes unprotected. Fix this by introducing a kernel-only descriptor_busy reference count in struct gpib_descriptor. Each handler atomically increments descriptor_busy under file_priv->descriptors_mutex before releasing the lock, and decrements it when done. close_dev_ioctl() checks descriptor_busy under the same lock and rejects the close with -EBUSY if the count is non-zero. A reference count rather than a simple flag is necessary because multiple handlers can operate on the same descriptor concurrently (e.g. IBRD and IBWAIT on the same handle from different threads). A separate counter is needed because io_in_progress can be cleared from unprivileged userspace via the IBWAIT ioctl (through general_ibstatus() with set_mask containing CMPL), which would allow an attacker to bypass a check based solely on io_in_progress. The new descriptor_busy counter is only modified by the kernel IO paths. The lock ordering is consistent (big_gpib_mutex -> descriptors_mutex) and the handlers only hold descriptors_mutex briefly during the lookup, so there is no deadlock risk and no impact on IO throughput. Signed-off-by: Adam Crosser Cc: stable Reviewed-by: Dave Penkler Tested-by: Dave Penkler Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 96 +++++++++++++++++++++++-------- drivers/gpib/include/gpib_types.h | 8 +++ 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index be757db993a56b..97c98f0a7a43b6 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -888,10 +888,6 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, read_cmd.handle); - if (!desc) - return -EINVAL; - if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) return -EFAULT; @@ -904,6 +900,17 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, read_cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + atomic_set(&desc->io_in_progress, 1); /* Read buffer loads till we fill the user supplied buffer */ @@ -937,6 +944,7 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (retval) @@ -964,10 +972,6 @@ static int command_ioctl(struct gpib_file_private *file_priv, if (cmd.completed_transfer_count > cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, cmd.handle); - if (!desc) - return -EINVAL; - userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; userbuf += cmd.completed_transfer_count; @@ -980,6 +984,17 @@ static int command_ioctl(struct gpib_file_private *file_priv, if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + /* * Write buffer loads till we empty the user supplied buffer. * Call drivers at least once, even if remain is zero, in @@ -1003,6 +1018,7 @@ static int command_ioctl(struct gpib_file_private *file_priv, userbuf += bytes_written; if (retval < 0) { atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); break; @@ -1022,6 +1038,7 @@ static int command_ioctl(struct gpib_file_private *file_priv, */ if (!no_clear_io_in_prog || fault) atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (fault) @@ -1047,10 +1064,6 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) return -EINVAL; - desc = handle_to_descriptor(file_priv, write_cmd.handle); - if (!desc) - return -EINVAL; - userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; userbuf += write_cmd.completed_transfer_count; @@ -1060,6 +1073,17 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b if (!access_ok(userbuf, remain)) return -EFAULT; + /* Lock descriptors to prevent concurrent close from freeing descriptor */ + if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) + return -ERESTARTSYS; + desc = handle_to_descriptor(file_priv, write_cmd.handle); + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); + atomic_set(&desc->io_in_progress, 1); /* Write buffer loads till we empty the user supplied buffer */ @@ -1094,6 +1118,7 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); atomic_set(&desc->io_in_progress, 0); + atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); if (fault) @@ -1276,6 +1301,9 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne { struct gpib_close_dev_ioctl cmd; struct gpib_file_private *file_priv = filep->private_data; + struct gpib_descriptor *desc; + unsigned int pad; + int sad; int retval; retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); @@ -1284,19 +1312,27 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) return -EINVAL; - if (!file_priv->descriptors[cmd.handle]) - return -EINVAL; - retval = decrement_open_device_count(board, &board->device_list, - file_priv->descriptors[cmd.handle]->pad, - file_priv->descriptors[cmd.handle]->sad); - if (retval < 0) - return retval; - - kfree(file_priv->descriptors[cmd.handle]); + mutex_lock(&file_priv->descriptors_mutex); + desc = file_priv->descriptors[cmd.handle]; + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EINVAL; + } + if (atomic_read(&desc->descriptor_busy)) { + mutex_unlock(&file_priv->descriptors_mutex); + return -EBUSY; + } + /* Remove from table while holding lock to prevent new IO from starting */ file_priv->descriptors[cmd.handle] = NULL; + pad = desc->pad; + sad = desc->sad; + mutex_unlock(&file_priv->descriptors_mutex); - return 0; + retval = decrement_open_device_count(board, &board->device_list, pad, sad); + + kfree(desc); + return retval; } static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) @@ -1331,12 +1367,25 @@ static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo if (retval) return -EFAULT; + /* + * Lock descriptors to prevent concurrent close from freeing + * descriptor. ibwait() releases big_gpib_mutex when wait_mask + * is non-zero, so desc must be pinned with descriptor_busy. + */ + mutex_lock(&file_priv->descriptors_mutex); desc = handle_to_descriptor(file_priv, wait_cmd.handle); - if (!desc) + if (!desc) { + mutex_unlock(&file_priv->descriptors_mutex); return -EINVAL; + } + atomic_inc(&desc->descriptor_busy); + mutex_unlock(&file_priv->descriptors_mutex); retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); + + atomic_dec(&desc->descriptor_busy); + if (retval < 0) return retval; @@ -2035,6 +2084,7 @@ void init_gpib_descriptor(struct gpib_descriptor *desc) desc->is_board = 0; desc->autopoll_enabled = 0; atomic_set(&desc->io_in_progress, 0); + atomic_set(&desc->descriptor_busy, 0); } int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module) diff --git a/drivers/gpib/include/gpib_types.h b/drivers/gpib/include/gpib_types.h index 5a0978ae27e791..28b73157ffb7e8 100644 --- a/drivers/gpib/include/gpib_types.h +++ b/drivers/gpib/include/gpib_types.h @@ -364,6 +364,14 @@ struct gpib_descriptor { unsigned int pad; /* primary gpib address */ int sad; /* secondary gpib address (negative means disabled) */ atomic_t io_in_progress; + /* + * Kernel-only reference count to prevent descriptor from being + * freed while IO handlers hold a pointer to it. Incremented + * before each IO operation, decremented when done. Unlike + * io_in_progress, this cannot be modified from userspace via + * general_ibstatus(). + */ + atomic_t descriptor_busy; unsigned is_board : 1; unsigned autopoll_enabled : 1; }; From 101ab946b79ad83b36d5cfd47de587492a80acf0 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 28 Jan 2026 15:00:10 +0000 Subject: [PATCH 379/700] comedi: ni_atmio16d: Fix invalid clean-up after failed attach If the driver's COMEDI "attach" handler function (`atmio16d_attach()`) returns an error, the COMEDI core will call the driver's "detach" handler function (`atmio16d_detach()`) to clean up. This calls `reset_atmio16d()` unconditionally, but depending on where the error occurred in the attach handler, the device may not have been sufficiently initialized to call `reset_atmio16d()`. It uses `dev->iobase` as the I/O port base address and `dev->private` as the pointer to the COMEDI device's private data structure. `dev->iobase` may still be set to its initial value of 0, which would result in undesired writes to low I/O port addresses. `dev->private` may still be `NULL`, which would result in null pointer dereferences. Fix `atmio16d_detach()` by checking that `dev->private` is valid (non-null) before calling `reset_atmio16d()`. This implies that `dev->iobase` was set correctly since that is set up before `dev->private`. Fixes: 2323b276308a ("Staging: comedi: add ni_at_atmio16d driver") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260128150011.5006-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_atmio16d.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index e5e7cc423c8796..b057b3b3582e17 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -698,7 +698,8 @@ static int atmio16d_attach(struct comedi_device *dev, static void atmio16d_detach(struct comedi_device *dev) { - reset_atmio16d(dev); + if (dev->private) + reset_atmio16d(dev); comedi_legacy_detach(dev); } From 3fb43a7a5b44713f892c58ead2e5f3a1bc9f4ee7 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 5 Feb 2026 13:39:49 +0000 Subject: [PATCH 380/700] comedi: me4000: Fix potential overrun of firmware buffer `me4000_xilinx_download()` loads the firmware that was requested by `request_firmware()`. It is possible for it to overrun the source buffer because it blindly trusts the file format. It reads a data stream length from the first 4 bytes into variable `file_length` and reads the data stream contents of length `file_length` from offset 16 onwards. Add a test to ensure that the supplied firmware is long enough to contain the header and the data stream. On failure, log an error and return `-EINVAL`. Note: The firmware loading was totally broken before commit ac584af59945 ("staging: comedi: me4000: fix firmware downloading"), but that is the most sensible target for this fix. Fixes: ac584af59945 ("staging: comedi: me4000: fix firmware downloading") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260205133949.71722-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/me4000.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c index 7dd3a007186300..effe9fdbbafe3c 100644 --- a/drivers/comedi/drivers/me4000.c +++ b/drivers/comedi/drivers/me4000.c @@ -315,6 +315,18 @@ static int me4000_xilinx_download(struct comedi_device *dev, unsigned int val; unsigned int i; + /* Get data stream length from header. */ + if (size >= 4) { + file_length = (((unsigned int)data[0] & 0xff) << 24) + + (((unsigned int)data[1] & 0xff) << 16) + + (((unsigned int)data[2] & 0xff) << 8) + + ((unsigned int)data[3] & 0xff); + } + if (size < 16 || file_length > size - 16) { + dev_err(dev->class_dev, "Firmware length inconsistency\n"); + return -EINVAL; + } + if (!xilinx_iobase) return -ENODEV; @@ -346,10 +358,6 @@ static int me4000_xilinx_download(struct comedi_device *dev, outl(val, devpriv->plx_regbase + PLX9052_CNTRL); /* Download Xilinx firmware */ - file_length = (((unsigned int)data[0] & 0xff) << 24) + - (((unsigned int)data[1] & 0xff) << 16) + - (((unsigned int)data[2] & 0xff) << 8) + - ((unsigned int)data[3] & 0xff); usleep_range(10, 1000); for (i = 0; i < file_length; i++) { From cc797d4821c754c701d9714b58bea947e31dbbe0 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Thu, 5 Feb 2026 14:01:30 +0000 Subject: [PATCH 381/700] comedi: me_daq: Fix potential overrun of firmware buffer `me2600_xilinx_download()` loads the firmware that was requested by `request_firmware()`. It is possible for it to overrun the source buffer because it blindly trusts the file format. It reads a data stream length from the first 4 bytes into variable `file_length` and reads the data stream contents of length `file_length` from offset 16 onwards. Although it checks that the supplied firmware is at least 16 bytes long, it does not check that it is long enough to contain the data stream. Add a test to ensure that the supplied firmware is long enough to contain the header and the data stream. On failure, log an error and return `-EINVAL`. Fixes: 85acac61096f9 ("Staging: comedi: add me_daq driver") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260205140130.76697-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/me_daq.c | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/comedi/drivers/me_daq.c b/drivers/comedi/drivers/me_daq.c index 076b15097afd9f..2f2ea029cffc2a 100644 --- a/drivers/comedi/drivers/me_daq.c +++ b/drivers/comedi/drivers/me_daq.c @@ -344,6 +344,25 @@ static int me2600_xilinx_download(struct comedi_device *dev, unsigned int file_length; unsigned int i; + /* + * Format of the firmware + * Build longs from the byte-wise coded header + * Byte 1-3: length of the array + * Byte 4-7: version + * Byte 8-11: date + * Byte 12-15: reserved + */ + if (size >= 4) { + file_length = (((unsigned int)data[0] & 0xff) << 24) + + (((unsigned int)data[1] & 0xff) << 16) + + (((unsigned int)data[2] & 0xff) << 8) + + ((unsigned int)data[3] & 0xff); + } + if (size < 16 || file_length > size - 16) { + dev_err(dev->class_dev, "Firmware length inconsistency\n"); + return -EINVAL; + } + /* disable irq's on PLX */ writel(0x00, devpriv->plx_regbase + PLX9052_INTCSR); @@ -357,22 +376,6 @@ static int me2600_xilinx_download(struct comedi_device *dev, writeb(0x00, dev->mmio + 0x0); sleep(1); - /* - * Format of the firmware - * Build longs from the byte-wise coded header - * Byte 1-3: length of the array - * Byte 4-7: version - * Byte 8-11: date - * Byte 12-15: reserved - */ - if (size < 16) - return -EINVAL; - - file_length = (((unsigned int)data[0] & 0xff) << 24) + - (((unsigned int)data[1] & 0xff) << 16) + - (((unsigned int)data[2] & 0xff) << 8) + - ((unsigned int)data[3] & 0xff); - /* * Loop for writing firmware byte by byte to xilinx * Firmware data start at offset 16 From 4b9a9a6d71e3e252032f959fb3895a33acb5865c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 25 Feb 2026 13:24:27 +0000 Subject: [PATCH 382/700] comedi: Reinit dev->spinlock between attachments to low-level drivers `struct comedi_device` is the main controlling structure for a COMEDI device created by the COMEDI subsystem. It contains a member `spinlock` containing a spin-lock that is initialized by the COMEDI subsystem, but is reserved for use by a low-level driver attached to the COMEDI device (at least since commit 25436dc9d84f ("Staging: comedi: remove RT code")). Some COMEDI devices (those created on initialization of the COMEDI subsystem when the "comedi.comedi_num_legacy_minors" parameter is non-zero) can be attached to different low-level drivers over their lifetime using the `COMEDI_DEVCONFIG` ioctl command. This can result in inconsistent lock states being reported when there is a mismatch in the spin-lock locking levels used by each low-level driver to which the COMEDI device has been attached. Fix it by reinitializing `dev->spinlock` before calling the low-level driver's `attach` function pointer if `CONFIG_LOCKDEP` is enabled. Reported-by: syzbot+cc9f7f4a7df09f53c4a4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=cc9f7f4a7df09f53c4a4 Fixes: ed9eccbe8970 ("Staging: add comedi core") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260225132427.86578-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index db225a3bf0128c..5ab96b5eefd135 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -1063,6 +1063,14 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) ret = -EIO; goto out; } + if (IS_ENABLED(CONFIG_LOCKDEP)) { + /* + * dev->spinlock is for private use by the attached low-level + * driver. Reinitialize it to stop lock-dependency tracking + * between attachments to different low-level drivers. + */ + spin_lock_init(&dev->spinlock); + } dev->driver = driv; dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr : dev->driver->driver_name; From 29f644f14b89e6c4965e3c89251929e451190a66 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 10 Mar 2026 11:11:04 +0000 Subject: [PATCH 383/700] comedi: runflags cannot determine whether to reclaim chanlist syzbot reported a memory leak [1], because commit 4e1da516debb ("comedi: Add reference counting for Comedi command handling") did not consider the exceptional exit case in do_cmd_ioctl() where runflags is not set. This caused chanlist not to be properly freed by do_become_nonbusy(), as it only frees chanlist when runflags is correctly set. Added a check in do_become_nonbusy() for the case where runflags is not set, to properly free the chanlist memory. [1] BUG: memory leak backtrace (crc 844a0efa): __comedi_get_user_chanlist drivers/comedi/comedi_fops.c:1815 [inline] do_cmd_ioctl.part.0+0x112/0x350 drivers/comedi/comedi_fops.c:1890 do_cmd_ioctl drivers/comedi/comedi_fops.c:1858 [inline] Fixes: 4e1da516debb ("comedi: Add reference counting for Comedi command handling") Reported-by: syzbot+f238baf6ded841b5a82e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f238baf6ded841b5a82e Signed-off-by: Edward Adam Davis Reviewed-by: Ian Abbott Cc: stable # 6.19 Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260310111104.70959-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/comedi_fops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 48a8a607a84c26..0df9f4636fb6bc 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -793,13 +793,15 @@ static void do_become_nonbusy(struct comedi_device *dev, __comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING | COMEDI_SRF_BUSY); spin_unlock_irqrestore(&s->spin_lock, flags); - if (comedi_is_runflags_busy(runflags)) { + if (async) { /* * "Run active" counter was set to 1 when setting up the * command. Decrement it and wait for it to become 0. */ - comedi_put_is_subdevice_running(s); - wait_for_completion(&async->run_complete); + if (comedi_is_runflags_busy(runflags)) { + comedi_put_is_subdevice_running(s); + wait_for_completion(&async->run_complete); + } comedi_buf_reset(s); async->inttrig = NULL; kfree(async->cmd.chanlist); From 93853512f565e625df2397f0d8050d6aafd7c3ad Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Mon, 9 Mar 2026 16:18:59 +0530 Subject: [PATCH 384/700] comedi: dt2815: add hardware detection to prevent crash The dt2815 driver crashes when attached to I/O ports without actual hardware present. This occurs because syzkaller or users can attach the driver to arbitrary I/O addresses via COMEDI_DEVCONFIG ioctl. When no hardware exists at the specified port, inb() operations return 0xff (floating bus), but outb() operations can trigger page faults due to undefined behavior, especially under race conditions: BUG: unable to handle page fault for address: 000000007fffff90 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page RIP: 0010:dt2815_attach+0x6e0/0x1110 Add hardware detection by reading the status register before attempting any write operations. If the read returns 0xff, assume no hardware is present and fail the attach with -ENODEV. This prevents crashes from outb() operations on non-existent hardware. Reported-by: syzbot+72f94b474d6e50b71ffc@syzkaller.appspotmail.com Cc: stable Closes: https://syzkaller.appspot.com/bug?extid=72f94b474d6e50b71ffc Tested-by: syzbot+72f94b474d6e50b71ffc@syzkaller.appspotmail.com Reviewed-by: Ian Abbott Signed-off-by: Deepanshu Kartikey Link: [https://lore.kernel.org/all/20260126070458.10974-1-kartikey406@gmail.com/T/] Link: [https://lore.kernel.org/all/20260126070458.10974-1-kartikey406@gmail.com/T/ Link: https://patch.msgid.link/20260309104859.503529-1-kartikey406@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt2815.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c index 03ba2fd18a2169..d066dc303520b9 100644 --- a/drivers/comedi/drivers/dt2815.c +++ b/drivers/comedi/drivers/dt2815.c @@ -175,6 +175,18 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it) ? current_range_type : voltage_range_type; } + /* + * Check if hardware is present before attempting any I/O operations. + * Reading 0xff from status register typically indicates no hardware + * on the bus (floating bus reads as all 1s). + */ + if (inb(dev->iobase + DT2815_STATUS) == 0xff) { + dev_err(dev->class_dev, + "No hardware detected at I/O base 0x%lx\n", + dev->iobase); + return -ENODEV; + } + /* Init the 2815 */ outb(0x00, dev->iobase + DT2815_STATUS); for (i = 0; i < 100; i++) { From ba2c83167b215da30fa2aae56b140198cf8d8408 Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Fri, 30 Jan 2026 07:41:40 +0800 Subject: [PATCH 385/700] misc: fastrpc: possible double-free of cctx->remote_heap fastrpc_init_create_static_process() may free cctx->remote_heap on the err_map path but does not clear the pointer. Later, fastrpc_rpmsg_remove() frees cctx->remote_heap again if it is non-NULL, which can lead to a double-free if the INIT_CREATE_STATIC ioctl hits the error path and the rpmsg device is subsequently removed/unbound. Clear cctx->remote_heap after freeing it in the error path to prevent the later cleanup from freeing it again. This issue was found by an in-house analysis workflow that extracts AST-based information and runs static checks, with LLM assistance for triage, and was confirmed by manual code review. No hardware testing was performed. Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd") Cc: stable@vger.kernel.org # 6.2+ Signed-off-by: Xingjing Deng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260129234140.410983-1-xjdeng@buaa.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 47356a5d580426..19cfc487df7cd8 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1401,6 +1401,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, } err_map: fastrpc_buf_free(fl->cctx->remote_heap); + fl->cctx->remote_heap = NULL; err_name: kfree(name); err: From 6a502776f4a4f80fb839b22f12aeaf0267fca344 Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Sat, 31 Jan 2026 14:55:39 +0800 Subject: [PATCH 386/700] misc: fastrpc: check qcom_scm_assign_mem() return in rpmsg_probe In the SDSP probe path, qcom_scm_assign_mem() is used to assign the reserved memory to the configured VMIDs, but its return value was not checked. Fail the probe if the SCM call fails to avoid continuing with an unexpected/incorrect memory permission configuration. This issue was found by an in-house analysis workflow that extracts AST-based information and runs static checks, with LLM assistance for triage, and was confirmed by manual code review. No hardware testing was performed. Fixes: c3c0363bc72d4 ("misc: fastrpc: support complete DMA pool access to the DSP") Cc: stable@vger.kernel.org # 6.11-rc1 Signed-off-by: Xingjing Deng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260131065539.2124047-1-xjdeng@buaa.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 19cfc487df7cd8..1080f9acf70a30 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2390,8 +2390,10 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) if (!err) { src_perms = BIT(QCOM_SCM_VMID_HLOS); - qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, + err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, data->vmperms, data->vmcount); + if (err) + goto err_free_data; } } From faeea8bbf6e958bf3c00cb08263109661975987c Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:15 -0700 Subject: [PATCH 387/700] net/sched: cls_fw: fix NULL pointer dereference on shared blocks The old-method path in fw_classify() calls tcf_block_q() and dereferences q->handle. Shared blocks leave block->q NULL, causing a NULL deref when an empty cls_fw filter is attached to a shared block and a packet with a nonzero major skb mark is classified. Reject the configuration in fw_change() when the old method (no TCA_OPTIONS) is used on a shared block, since fw_classify()'s old-method path needs block->q which is NULL for shared blocks. The fixed null-ptr-deref calling stack: KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f] RIP: 0010:fw_classify (net/sched/cls_fw.c:81) Call Trace: tcf_classify (./include/net/tc_wrapper.h:197 net/sched/cls_api.c:1764 net/sched/cls_api.c:1860) tc_run (net/core/dev.c:4401) __dev_queue_xmit (net/core/dev.c:4535 net/core/dev.c:4790) Fixes: 1abf272022cf ("net: sched: tcindex, fw, flow: use tcf_block_q helper to get struct Qdisc") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260331050217.504278-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/sched/cls_fw.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index be81c108179db0..23884ef8b80cea 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -247,8 +247,18 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, struct nlattr *tb[TCA_FW_MAX + 1]; int err; - if (!opt) - return handle ? -EINVAL : 0; /* Succeed if it is old method. */ + if (!opt) { + if (handle) + return -EINVAL; + + if (tcf_block_shared(tp->chain->block)) { + NL_SET_ERR_MSG(extack, + "Must specify mark when attaching fw filter to block"); + return -EINVAL; + } + + return 0; /* Succeed if it is old method. */ + } err = nla_parse_nested_deprecated(tb, TCA_FW_MAX, opt, fw_policy, NULL); From 1a280dd4bd1d616a01d6ffe0de284c907b555504 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:16 -0700 Subject: [PATCH 388/700] net/sched: cls_flow: fix NULL pointer dereference on shared blocks flow_change() calls tcf_block_q() and dereferences q->handle to derive a default baseclass. Shared blocks leave block->q NULL, causing a NULL deref when a flow filter without a fully qualified baseclass is created on a shared block. Check tcf_block_shared() before accessing block->q and return -EINVAL for shared blocks. This avoids the null-deref shown below: ======================================================================= KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f] RIP: 0010:flow_change (net/sched/cls_flow.c:508) Call Trace: tc_new_tfilter (net/sched/cls_api.c:2432) rtnetlink_rcv_msg (net/core/rtnetlink.c:6980) [...] ======================================================================= Fixes: 1abf272022cf ("net: sched: tcindex, fw, flow: use tcf_block_q helper to get struct Qdisc") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260331050217.504278-2-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/sched/cls_flow.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 339c664beff6dd..ab364e4e468624 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -503,8 +503,16 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, } if (TC_H_MAJ(baseclass) == 0) { - struct Qdisc *q = tcf_block_q(tp->chain->block); + struct tcf_block *block = tp->chain->block; + struct Qdisc *q; + if (tcf_block_shared(block)) { + NL_SET_ERR_MSG(extack, + "Must specify baseclass when attaching flow filter to block"); + goto err2; + } + + q = tcf_block_q(block); baseclass = TC_H_MAKE(q->handle, baseclass); } if (TC_H_MIN(baseclass) == 0) From 70f73562d278d9f88e7095e327f2a50082a82c65 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 30 Mar 2026 22:02:17 -0700 Subject: [PATCH 389/700] selftests/tc-testing: add tests for cls_fw and cls_flow on shared blocks Regression tests for the shared-block NULL derefs fixed in the previous two patches: - fw: attempt to attach an empty fw filter to a shared block and verify the configuration is rejected with EINVAL. - flow: create a flow filter on a shared block without a baseclass and verify the configuration is rejected with EINVAL. Signed-off-by: Xiang Mei Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260331050217.504278-3-xmei5@asu.edu Signed-off-by: Paolo Abeni --- .../tc-testing/tc-tests/infra/filter.json | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json index 8d10042b489b1a..dbce6436ed26ff 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json @@ -22,5 +22,49 @@ "teardown": [ "$TC qdisc del dev $DUMMY root handle 1: htb default 1" ] + }, + { + "id": "b7e3", + "name": "Empty fw filter on shared block - rejected at config time", + "category": [ + "filter", + "fw" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 egress_block 1 clsact" + ], + "cmdUnderTest": "$TC filter add block 1 protocol ip prio 1 fw", + "expExitCode": "2", + "verifyCmd": "$TC filter show block 1", + "matchPattern": "fw", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] + }, + { + "id": "c8f4", + "name": "Flow filter on shared block without baseclass - rejected at config time", + "category": [ + "filter", + "flow" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress_block 1 clsact" + ], + "cmdUnderTest": "$TC filter add block 1 protocol ip prio 1 handle 1 flow map key dst", + "expExitCode": "2", + "verifyCmd": "$TC filter show block 1", + "matchPattern": "flow", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] From 48b5163c957548f5854f14c90bfdedc33afbea3c Mon Sep 17 00:00:00 2001 From: Christian Eggers Date: Fri, 27 Mar 2026 13:16:44 +0000 Subject: [PATCH 390/700] nvmem: imx: assign nvmem_cell_info::raw_len Avoid getting error messages at startup like the following on i.MX6ULL: nvmem imx-ocotp0: cell mac-addr raw len 6 unaligned to nvmem word size 4 nvmem imx-ocotp0: cell mac-addr raw len 6 unaligned to nvmem word size 4 This shouldn't cause any functional change as this alignment would otherwise be done in nvmem_cell_info_to_nvmem_cell_entry_nodup(). Cc: stable@vger.kernel.org Fixes: 13bcd440f2ff ("nvmem: core: verify cell's raw_len") Signed-off-by: Christian Eggers Signed-off-by: Fabio Estevam Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131645.3025781-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp-ele.c | 1 + drivers/nvmem/imx-ocotp.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c index 7cf7e809a8f51a..a0d2985c6d0304 100644 --- a/drivers/nvmem/imx-ocotp-ele.c +++ b/drivers/nvmem/imx-ocotp-ele.c @@ -131,6 +131,7 @@ static int imx_ocotp_cell_pp(void *context, const char *id, int index, static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, struct nvmem_cell_info *cell) { + cell->raw_len = round_up(cell->bytes, 4); cell->read_post_process = imx_ocotp_cell_pp; } diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 7bf7656d4f9631..108d78d7f6cba6 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -589,6 +589,7 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, struct nvmem_cell_info *cell) { + cell->raw_len = round_up(cell->bytes, 4); cell->read_post_process = imx_ocotp_cell_pp; } From f9b88613ff402aa6fe8fd020573cb95867ae947e Mon Sep 17 00:00:00 2001 From: Ivan Vera Date: Fri, 27 Mar 2026 13:16:45 +0000 Subject: [PATCH 391/700] nvmem: zynqmp_nvmem: Fix buffer size in DMA and memcpy Buffer size used in dma allocation and memcpy is wrong. It can lead to undersized DMA buffer access and possible memory corruption. use correct buffer size in dma_alloc_coherent and memcpy. Fixes: 737c0c8d07b5 ("nvmem: zynqmp_nvmem: Add support to access efuse") Cc: stable@vger.kernel.org Signed-off-by: Ivan Vera Signed-off-by: Harish Ediga Signed-off-by: Harsh Jain Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260327131645.3025781-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/zynqmp_nvmem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c index 7da717d6c7faf3..d297ff150dc024 100644 --- a/drivers/nvmem/zynqmp_nvmem.c +++ b/drivers/nvmem/zynqmp_nvmem.c @@ -66,7 +66,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, dma_addr_t dma_buf; size_t words = bytes / WORD_INBYTES; int ret; - int value; + unsigned int value; char *data; if (bytes % WORD_INBYTES != 0) { @@ -80,7 +80,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, } if (pufflag == 1 && flag == EFUSE_WRITE) { - memcpy(&value, val, bytes); + memcpy(&value, val, sizeof(value)); if ((offset == EFUSE_PUF_START_OFFSET || offset == EFUSE_PUF_MID_OFFSET) && value & P_USER_0_64_UPPER_MASK) { @@ -100,7 +100,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, if (!efuse) return -ENOMEM; - data = dma_alloc_coherent(dev, sizeof(bytes), + data = dma_alloc_coherent(dev, bytes, &dma_buf, GFP_KERNEL); if (!data) { ret = -ENOMEM; @@ -134,7 +134,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset, if (flag == EFUSE_READ) memcpy(val, data, bytes); efuse_access_err: - dma_free_coherent(dev, sizeof(bytes), + dma_free_coherent(dev, bytes, data, dma_buf); efuse_data_fail: dma_free_coherent(dev, sizeof(struct xilinx_efuse), From d78ceee1e6205ffcd84ff581ccb40a008d39136f Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Tue, 24 Mar 2026 08:29:28 +0000 Subject: [PATCH 392/700] .get_maintainer.ignore: add myself I don't want get_maintainer.pl to automatically print my email. Signed-off-by: Askar Safin Link: https://patch.msgid.link/20260324082928.3473789-1-safinaskar@gmail.com Signed-off-by: Greg Kroah-Hartman --- .get_maintainer.ignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.get_maintainer.ignore b/.get_maintainer.ignore index e8d2269bad9d00..60b6b2a374cd6e 100644 --- a/.get_maintainer.ignore +++ b/.get_maintainer.ignore @@ -1,6 +1,7 @@ Alan Cox Alan Cox Alyssa Rosenzweig +Askar Safin Christoph Hellwig Jeff Kirsher Marc Gonzalez From b18c833888742ca9de80c250f9d40d0e97caa9f6 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 1 Apr 2026 11:21:53 +0200 Subject: [PATCH 393/700] vsock: initialize child_ns_mode_locked in vsock_net_init() The `child_ns_mode_locked` field lives in `struct net`, which persists across vsock module reloads. When the module is unloaded and reloaded, `vsock_net_init()` resets `mode` and `child_ns_mode` back to their default values, but does not reset `child_ns_mode_locked`. The stale lock from the previous module load causes subsequent writes to `child_ns_mode` to silently fail: `vsock_net_set_child_mode()` sees the old lock, skips updating the actual value, and returns success when the requested mode matches the stale lock. The sysctl handler reports no error, but `child_ns_mode` remains unchanged. Steps to reproduce: $ modprobe vsock $ echo local > /proc/sys/net/vsock/child_ns_mode $ cat /proc/sys/net/vsock/child_ns_mode local $ modprobe -r vsock $ modprobe vsock $ echo local > /proc/sys/net/vsock/child_ns_mode $ cat /proc/sys/net/vsock/child_ns_mode global <--- expected "local" Fix this by initializing `child_ns_mode_locked` to 0 (unlocked) in `vsock_net_init()`, so the write-once mechanism works correctly after module reload. Fixes: 102eab95f025 ("vsock: lock down child_ns_mode as write-once") Reported-by: Jin Liu Signed-off-by: Stefano Garzarella Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/20260401092153.28462-1-sgarzare@redhat.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/af_vsock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 2f7d94d682cbed..d912ed2f012a3e 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -2928,6 +2928,7 @@ static void vsock_net_init(struct net *net) net->vsock.mode = vsock_net_child_mode(current->nsproxy->net_ns); net->vsock.child_ns_mode = net->vsock.mode; + net->vsock.child_ns_mode_locked = 0; } static __net_init int vsock_sysctl_init_net(struct net *net) From f5df2990c364d1ac596d24b3118dbc56503f7cd4 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Wed, 1 Apr 2026 11:22:42 +0200 Subject: [PATCH 394/700] net: hsr: serialize seq_blocks merge across nodes During node merging, hsr_handle_sup_frame() walks node_curr->seq_blocks to update node_real without holding node_curr->seq_out_lock. This allows concurrent mutations from duplicate registration paths, risking inconsistent state or XArray/bitmap corruption. Fix this by locking both nodes' seq_out_lock during the merge. To prevent ABBA deadlocks, locks are acquired in order of memory address. Reviewed-by: Felix Maurer Fixes: 415e6367512b ("hsr: Implement more robust duplicate discard for PRP") Signed-off-by: Luka Gejak Link: https://patch.msgid.link/20260401092243.52121-2-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski --- net/hsr/hsr_framereg.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 50996f4de7f9e7..d418635936743a 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -123,6 +123,40 @@ static void hsr_free_node_rcu(struct rcu_head *rn) hsr_free_node(node); } +static void hsr_lock_seq_out_pair(struct hsr_node *node_a, + struct hsr_node *node_b) +{ + if (node_a == node_b) { + spin_lock_bh(&node_a->seq_out_lock); + return; + } + + if (node_a < node_b) { + spin_lock_bh(&node_a->seq_out_lock); + spin_lock_nested(&node_b->seq_out_lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock_bh(&node_b->seq_out_lock); + spin_lock_nested(&node_a->seq_out_lock, SINGLE_DEPTH_NESTING); + } +} + +static void hsr_unlock_seq_out_pair(struct hsr_node *node_a, + struct hsr_node *node_b) +{ + if (node_a == node_b) { + spin_unlock_bh(&node_a->seq_out_lock); + return; + } + + if (node_a < node_b) { + spin_unlock(&node_b->seq_out_lock); + spin_unlock_bh(&node_a->seq_out_lock); + } else { + spin_unlock(&node_a->seq_out_lock); + spin_unlock_bh(&node_b->seq_out_lock); + } +} + void hsr_del_nodes(struct list_head *node_db) { struct hsr_node *node; @@ -432,7 +466,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) } ether_addr_copy(node_real->macaddress_B, ethhdr->h_source); - spin_lock_bh(&node_real->seq_out_lock); + hsr_lock_seq_out_pair(node_real, node_curr); for (i = 0; i < HSR_PT_PORTS; i++) { if (!node_curr->time_in_stale[i] && time_after(node_curr->time_in[i], node_real->time_in[i])) { @@ -455,7 +489,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE); } } - spin_unlock_bh(&node_real->seq_out_lock); + hsr_unlock_seq_out_pair(node_real, node_curr); node_real->addr_B_port = port_rcv->type; spin_lock_bh(&hsr->list_lock); From 2e3514e63bfb0e972b1f19668547a455d0129e88 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Wed, 1 Apr 2026 11:22:43 +0200 Subject: [PATCH 395/700] net: hsr: fix VLAN add unwind on slave errors When vlan_vid_add() fails for a secondary slave, the error path calls vlan_vid_del() on the failing port instead of the peer slave that had already succeeded. This results in asymmetric VLAN state across the HSR pair. Fix this by switching to a centralized unwind path that removes the VID from any slave device that was already programmed. Fixes: 1a8a63a5305e ("net: hsr: Add VLAN CTAG filter support") Signed-off-by: Luka Gejak Link: https://patch.msgid.link/20260401092243.52121-3-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski --- net/hsr/hsr_device.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index d1bfc49b5f017b..fd2fea25eff0dd 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -532,8 +532,8 @@ static void hsr_change_rx_flags(struct net_device *dev, int change) static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { - bool is_slave_a_added = false; - bool is_slave_b_added = false; + struct net_device *slave_a_dev = NULL; + struct net_device *slave_b_dev = NULL; struct hsr_port *port; struct hsr_priv *hsr; int ret = 0; @@ -549,33 +549,35 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev, switch (port->type) { case HSR_PT_SLAVE_A: if (ret) { - /* clean up Slave-B */ netdev_err(dev, "add vid failed for Slave-A\n"); - if (is_slave_b_added) - vlan_vid_del(port->dev, proto, vid); - return ret; + goto unwind; } - - is_slave_a_added = true; + slave_a_dev = port->dev; break; - case HSR_PT_SLAVE_B: if (ret) { - /* clean up Slave-A */ netdev_err(dev, "add vid failed for Slave-B\n"); - if (is_slave_a_added) - vlan_vid_del(port->dev, proto, vid); - return ret; + goto unwind; } - - is_slave_b_added = true; + slave_b_dev = port->dev; break; default: + if (ret) + goto unwind; break; } } return 0; + +unwind: + if (slave_a_dev) + vlan_vid_del(slave_a_dev, proto, vid); + + if (slave_b_dev) + vlan_vid_del(slave_b_dev, proto, vid); + + return ret; } static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev, From 4e453375561fc60820e6b9d8ebeb6b3ee177d42e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Apr 2026 15:47:21 +0000 Subject: [PATCH 396/700] ipv6: avoid overflows in ip6_datagram_send_ctl() Yiming Qian reported : I believe I found a locally triggerable kernel bug in the IPv6 sendmsg ancillary-data path that can panic the kernel via `skb_under_panic()` (local DoS). The core issue is a mismatch between: - a 16-bit length accumulator (`struct ipv6_txoptions::opt_flen`, type `__u16`) and - a pointer to the *last* provided destination-options header (`opt->dst1opt`) when multiple `IPV6_DSTOPTS` control messages (cmsgs) are provided. - `include/net/ipv6.h`: - `struct ipv6_txoptions::opt_flen` is `__u16` (wrap possible). (lines 291-307, especially 298) - `net/ipv6/datagram.c:ip6_datagram_send_ctl()`: - Accepts repeated `IPV6_DSTOPTS` and accumulates into `opt_flen` without rejecting duplicates. (lines 909-933) - `net/ipv6/ip6_output.c:__ip6_append_data()`: - Uses `opt->opt_flen + opt->opt_nflen` to compute header sizes/headroom decisions. (lines 1448-1466, especially 1463-1465) - `net/ipv6/ip6_output.c:__ip6_make_skb()`: - Calls `ipv6_push_frag_opts()` if `opt->opt_flen` is non-zero. (lines 1930-1934) - `net/ipv6/exthdrs.c:ipv6_push_frag_opts()` / `ipv6_push_exthdr()`: - Push size comes from `ipv6_optlen(opt->dst1opt)` (based on the pointed-to header). (lines 1179-1185 and 1206-1211) 1. `opt_flen` is a 16-bit accumulator: - `include/net/ipv6.h:298` defines `__u16 opt_flen; /* after fragment hdr */`. 2. `ip6_datagram_send_ctl()` accepts *repeated* `IPV6_DSTOPTS` cmsgs and increments `opt_flen` each time: - In `net/ipv6/datagram.c:909-933`, for `IPV6_DSTOPTS`: - It computes `len = ((hdr->hdrlen + 1) << 3);` - It checks `CAP_NET_RAW` using `ns_capable(net->user_ns, CAP_NET_RAW)`. (line 922) - Then it does: - `opt->opt_flen += len;` (line 927) - `opt->dst1opt = hdr;` (line 928) There is no duplicate rejection here (unlike the legacy `IPV6_2292DSTOPTS` path which rejects duplicates at `net/ipv6/datagram.c:901-904`). If enough large `IPV6_DSTOPTS` cmsgs are provided, `opt_flen` wraps while `dst1opt` still points to a large (2048-byte) destination-options header. In the attached PoC (`poc.c`): - 32 cmsgs with `hdrlen=255` => `len = (255+1)*8 = 2048` - 1 cmsg with `hdrlen=0` => `len = 8` - Total increment: `32*2048 + 8 = 65544`, so `(__u16)opt_flen == 8` - The last cmsg is 2048 bytes, so `dst1opt` points to a 2048-byte header. 3. The transmit path sizes headers using the wrapped `opt_flen`: - In `net/ipv6/ip6_output.c:1463-1465`: - `headersize = sizeof(struct ipv6hdr) + (opt ? opt->opt_flen + opt->opt_nflen : 0) + ...;` With wrapped `opt_flen`, `headersize`/headroom decisions underestimate what will be pushed later. 4. When building the final skb, the actual push length comes from `dst1opt` and is not limited by wrapped `opt_flen`: - In `net/ipv6/ip6_output.c:1930-1934`: - `if (opt->opt_flen) proto = ipv6_push_frag_opts(skb, opt, proto);` - In `net/ipv6/exthdrs.c:1206-1211`, `ipv6_push_frag_opts()` pushes `dst1opt` via `ipv6_push_exthdr()`. - In `net/ipv6/exthdrs.c:1179-1184`, `ipv6_push_exthdr()` does: - `skb_push(skb, ipv6_optlen(opt));` - `memcpy(h, opt, ipv6_optlen(opt));` With insufficient headroom, `skb_push()` underflows and triggers `skb_under_panic()` -> `BUG()`: - `net/core/skbuff.c:2669-2675` (`skb_push()` calls `skb_under_panic()`) - `net/core/skbuff.c:207-214` (`skb_panic()` ends in `BUG()`) - The `IPV6_DSTOPTS` cmsg path requires `CAP_NET_RAW` in the target netns user namespace (`ns_capable(net->user_ns, CAP_NET_RAW)`). - Root (or any task with `CAP_NET_RAW`) can trigger this without user namespaces. - An unprivileged `uid=1000` user can trigger this if unprivileged user namespaces are enabled and it can create a userns+netns to obtain namespaced `CAP_NET_RAW` (the attached PoC does this). - Local denial of service: kernel BUG/panic (system crash). - Reproducible with a small userspace PoC. This patch does not reject duplicated options, as this might break some user applications. Instead, it makes sure to adjust opt_flen and opt_nflen to correctly reflect the size of the current option headers, preventing the overflows and the potential for panics. This applies to IPV6_DSTOPTS, IPV6_HOPOPTS, and IPV6_RTHDR. Specifically: When a new IPV6_DSTOPTS is processed, the length of the old opt->dst1opt is subtracted from opt->opt_flen before adding the new length. When a new IPV6_HOPOPTS is processed, the length of the old opt->dst0opt is subtracted from opt->opt_nflen. When a new Routing Header (IPV6_RTHDR or IPV6_2292RTHDR) is processed, the length of the old opt->srcrt is subtracted from opt->opt_nflen. In the special case within IPV6_2292RTHDR handling where dst1opt is moved to dst0opt, the length of the old opt->dst0opt is subtracted from opt->opt_nflen before the new one is added. Fixes: 333fad5364d6 ("[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).") Reported-by: Yiming Qian Closes: https://lore.kernel.org/netdev/CAL_bE8JNzawgr5OX5m+3jnQDHry2XxhQT5=jThW1zDPtUikRYA@mail.gmail.com/ Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260401154721.3740056-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/datagram.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index c564b68a056268..993e2d76fc1f66 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -763,6 +763,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, { struct in6_pktinfo *src_info; struct cmsghdr *cmsg; + struct ipv6_rt_hdr *orthdr; struct ipv6_rt_hdr *rthdr; struct ipv6_opt_hdr *hdr; struct ipv6_txoptions *opt = ipc6->opt; @@ -924,9 +925,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, goto exit_f; } if (cmsg->cmsg_type == IPV6_DSTOPTS) { + if (opt->dst1opt) + opt->opt_flen -= ipv6_optlen(opt->dst1opt); opt->opt_flen += len; opt->dst1opt = hdr; } else { + if (opt->dst0opt) + opt->opt_nflen -= ipv6_optlen(opt->dst0opt); opt->opt_nflen += len; opt->dst0opt = hdr; } @@ -969,12 +974,17 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, goto exit_f; } + orthdr = opt->srcrt; + if (orthdr) + opt->opt_nflen -= ((orthdr->hdrlen + 1) << 3); opt->opt_nflen += len; opt->srcrt = rthdr; if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) { int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3); + if (opt->dst0opt) + opt->opt_nflen -= ipv6_optlen(opt->dst0opt); opt->opt_nflen += dsthdrlen; opt->dst0opt = opt->dst1opt; opt->dst1opt = NULL; From 3a359bf5c61d52e7f09754108309d637532164a6 Mon Sep 17 00:00:00 2001 From: Ruide Cao Date: Thu, 2 Apr 2026 23:12:31 +0800 Subject: [PATCH 397/700] batman-adv: reject oversized global TT response buffers batadv_tt_prepare_tvlv_global_data() builds the allocation length for a global TT response in 16-bit temporaries. When a remote originator advertises a large enough global TT, the TT payload length plus the VLAN header offset can exceed 65535 and wrap before kmalloc(). The full-table response path still uses the original TT payload length when it fills tt_change, so the wrapped allocation is too small and batadv_tt_prepare_tvlv_global_data() writes past the end of the heap object before the later packet-size check runs. Fix this by rejecting TT responses whose TVLV value length cannot fit in the 16-bit TVLV payload length field. Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Ruide Cao Signed-off-by: Ren Wei Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/translation-table.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 6e95e883c2bf08..05cddcf994f651 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -798,8 +798,8 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, { u16 num_vlan = 0; u16 num_entries = 0; - u16 change_offset; - u16 tvlv_len; + u16 tvlv_len = 0; + unsigned int change_offset; struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_orig_node_vlan *vlan; u8 *tt_change_ptr; @@ -816,6 +816,11 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, if (*tt_len < 0) *tt_len = batadv_tt_len(num_entries); + if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) { + *tt_len = 0; + goto out; + } + tvlv_len = *tt_len; tvlv_len += change_offset; From 6ec1235fc941dac6c011b30ee01d9220ff87e0cd Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:06 +0000 Subject: [PATCH 398/700] ASoC: qcom: q6apm: move component registration to unmanaged version q6apm component registers dais dynamically from ASoC toplology, which are allocated using device managed version apis. Allocating both component and dynamic dais using managed version could lead to incorrect free ordering, dai will be freed while component still holding references to it. Fix this issue by moving component to unmanged version so that the dai pointers are only freeded after the component is removed. ================================================================== BUG: KASAN: slab-use-after-free in snd_soc_del_component_unlocked+0x3d4/0x400 [snd_soc_core] Read of size 8 at addr ffff00084493a6e8 by task kworker/u48:0/3426 Tainted: [W]=WARN Hardware name: LENOVO 21N2ZC5PUS/21N2ZC5PUS, BIOS N42ET57W (1.31 ) 08/08/2024 Workqueue: pdr_notifier_wq pdr_notifier_work [pdr_interface] Call trace: show_stack+0x28/0x7c (C) dump_stack_lvl+0x60/0x80 print_report+0x160/0x4b4 kasan_report+0xac/0xfc __asan_report_load8_noabort+0x20/0x34 snd_soc_del_component_unlocked+0x3d4/0x400 [snd_soc_core] snd_soc_unregister_component_by_driver+0x50/0x88 [snd_soc_core] devm_component_release+0x30/0x5c [snd_soc_core] devres_release_all+0x13c/0x210 device_unbind_cleanup+0x20/0x190 device_release_driver_internal+0x350/0x468 device_release_driver+0x18/0x30 bus_remove_device+0x1a0/0x35c device_del+0x314/0x7f0 device_unregister+0x20/0xbc apr_remove_device+0x5c/0x7c [apr] device_for_each_child+0xd8/0x160 apr_pd_status+0x7c/0xa8 [apr] pdr_notifier_work+0x114/0x240 [pdr_interface] process_one_work+0x500/0xb70 worker_thread+0x630/0xfb0 kthread+0x370/0x6c0 ret_from_fork+0x10/0x20 Allocated by task 77: kasan_save_stack+0x40/0x68 kasan_save_track+0x20/0x40 kasan_save_alloc_info+0x44/0x58 __kasan_kmalloc+0xbc/0xdc __kmalloc_node_track_caller_noprof+0x1f4/0x620 devm_kmalloc+0x7c/0x1c8 snd_soc_register_dai+0x50/0x4f0 [snd_soc_core] soc_tplg_pcm_elems_load+0x55c/0x1eb8 [snd_soc_core] snd_soc_tplg_component_load+0x4f8/0xb60 [snd_soc_core] audioreach_tplg_init+0x124/0x1fc [snd_q6apm] q6apm_audio_probe+0x10/0x1c [snd_q6apm] snd_soc_component_probe+0x5c/0x118 [snd_soc_core] soc_probe_component+0x44c/0xaf0 [snd_soc_core] snd_soc_bind_card+0xad0/0x2370 [snd_soc_core] snd_soc_register_card+0x3b0/0x4c0 [snd_soc_core] devm_snd_soc_register_card+0x50/0xc8 [snd_soc_core] x1e80100_platform_probe+0x208/0x368 [snd_soc_x1e80100] platform_probe+0xc0/0x188 really_probe+0x188/0x804 __driver_probe_device+0x158/0x358 driver_probe_device+0x60/0x190 __device_attach_driver+0x16c/0x2a8 bus_for_each_drv+0x100/0x194 __device_attach+0x174/0x380 device_initial_probe+0x14/0x20 bus_probe_device+0x124/0x154 deferred_probe_work_func+0x140/0x220 process_one_work+0x500/0xb70 worker_thread+0x630/0xfb0 kthread+0x370/0x6c0 ret_from_fork+0x10/0x20 Freed by task 3426: kasan_save_stack+0x40/0x68 kasan_save_track+0x20/0x40 __kasan_save_free_info+0x4c/0x80 __kasan_slab_free+0x78/0xa0 kfree+0x100/0x4a4 devres_release_all+0x144/0x210 device_unbind_cleanup+0x20/0x190 device_release_driver_internal+0x350/0x468 device_release_driver+0x18/0x30 bus_remove_device+0x1a0/0x35c device_del+0x314/0x7f0 device_unregister+0x20/0xbc apr_remove_device+0x5c/0x7c [apr] device_for_each_child+0xd8/0x160 apr_pd_status+0x7c/0xa8 [apr] pdr_notifier_work+0x114/0x240 [pdr_interface] process_one_work+0x500/0xb70 worker_thread+0x630/0xfb0 kthread+0x370/0x6c0 ret_from_fork+0x10/0x20 Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 970b08c89bb355..069048db536711 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -747,13 +747,22 @@ static int apm_probe(gpr_device_t *gdev) q6apm_get_apm_state(apm); - ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); + ret = snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); if (ret < 0) { dev_err(dev, "failed to register q6apm: %d\n", ret); return ret; } - return of_platform_populate(dev->of_node, NULL, NULL, dev); + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + snd_soc_unregister_component(dev); + + return ret; +} + +static void apm_remove(gpr_device_t *gdev) +{ + snd_soc_unregister_component(&gdev->dev); } struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid) @@ -820,6 +829,7 @@ MODULE_DEVICE_TABLE(of, apm_device_id); static gpr_driver_t apm_driver = { .probe = apm_probe, + .remove = apm_remove, .gpr_callback = apm_callback, .driver = { .name = "qcom-apm", From 4a0e1bcc98f7281d1605768bd2fe71eacc34f9b7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:07 +0000 Subject: [PATCH 399/700] ASoC: qcom: q6apm: remove child devices when apm is removed looks like q6apm driver does not remove the child driver q6apm-dai and q6apm-bedais when the this driver is removed. Fix this by depopulating them in remove callback. With this change when the dsp is shutdown all the devices associated with q6apm will now be removed. Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 069048db536711..2dc525c8be42ba 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -762,6 +762,7 @@ static int apm_probe(gpr_device_t *gdev) static void apm_remove(gpr_device_t *gdev) { + of_platform_depopulate(&gdev->dev); snd_soc_unregister_component(&gdev->dev); } From d5bfdd28e0cdd45043ae6e0ac168a451d59283dc Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:08 +0000 Subject: [PATCH 400/700] ASoC: qcom: qdsp6: topology: check widget type before accessing data Check widget type before accessing the private data, as this could a virtual widget which is no associated with a dsp graph, container and module. Accessing witout check could lead to incorrect memory access. Fixes: 36ad9bf1d93d ("ASoC: qdsp6: audioreach: add topology support") Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index e732fac9b8ca05..1f69fba6de26d1 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -952,9 +952,6 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp, struct audioreach_container *cont; struct audioreach_module *mod; - mod = dobj->private; - cont = mod->container; - if (w->id == snd_soc_dapm_mixer) { /* virtual widget */ struct snd_ar_control *scontrol = dobj->private; @@ -963,6 +960,11 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp, kfree(scontrol); return 0; } + mod = dobj->private; + if (!mod) + return 0; + + cont = mod->container; mutex_lock(&apm->lock); idr_remove(&apm->modules_idr, mod->instance_id); From 69acc488aaf39d0ddf6c3cf0e47c1873d39919a2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:09 +0000 Subject: [PATCH 401/700] ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens As prepare can be called mulitple times, this can result in multiple graph opens for playback path. This will result in a memory leaks, fix this by adding a check before opening. Fixes: be1fae62cf25 ("ASoC: q6apm-lpass-dai: close graph on prepare errors") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-5-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); From cab45ab95ce7600fc0ff84585c77fd45b7b0d67c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:10 +0000 Subject: [PATCH 402/700] ASoC: qcom: q6apm-dai: reset queue ptr on trigger stop Reset queue pointer on SNDRV_PCM_TRIGGER_STOP event to be inline with resetting appl_ptr. Without this we will end up with a queue_ptr out of sync and driver could try to send data that is not ready yet. Fix this by resetting the queue_ptr. Fixes: 3d4a4411aa8bb ("ASoC: q6apm-dai: schedule all available frames to avoid dsp under-runs") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-6-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-dai.c | 1 + sound/soc/qcom/qdsp6/q6apm.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 306e928e7b49b5..292be457764f6f 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -317,6 +317,7 @@ static int q6apm_dai_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: /* TODO support be handled via SoftPause Module */ prtd->state = Q6APM_STREAM_STOPPED; + prtd->queue_ptr = 0; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2dc525c8be42ba..5751e80b3b9289 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -215,6 +215,8 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a mutex_lock(&graph->lock); + data->dsp_buf = 0; + if (data->buf) { mutex_unlock(&graph->lock); return 0; From e46957f27c6004f3ab5ec456f4e848950364ebc1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:11 +0000 Subject: [PATCH 403/700] ASoC: dt-bindings: qcom: add LPASS LPI MI2S dai ids Add new dai ids entries for LPASS LPI MI2S and SENARY MI2S audio lines. Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Signed-off-by: Srinivas Kandagatla Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260402081118.348071-7-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,q6dsp-lpass-ports.yaml | 5 ++++- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 08c618e7e4281d..2b27d6c8f58f14 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -126,13 +126,16 @@ patternProperties: reg: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and - # QUINARY_MI2S_RX - QUINARY_MI2S_TX + # QUINARY_MI2S_RX - QUINARY_MI2S_TX and + # LPI_MI2S_RX_0 - SENARY_MI2S_TX items: oneOf: - minimum: 16 maximum: 23 - minimum: 127 maximum: 128 + - minimum: 137 + maximum: 148 then: required: - qcom,sd-lines diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 6d1ce7f5da51c6..45850f2d434258 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -140,6 +140,18 @@ #define DISPLAY_PORT_RX_6 134 #define DISPLAY_PORT_RX_7 135 #define USB_RX 136 +#define LPI_MI2S_RX_0 137 +#define LPI_MI2S_TX_0 138 +#define LPI_MI2S_RX_1 139 +#define LPI_MI2S_TX_1 140 +#define LPI_MI2S_RX_2 141 +#define LPI_MI2S_TX_2 142 +#define LPI_MI2S_RX_3 143 +#define LPI_MI2S_TX_3 144 +#define LPI_MI2S_RX_4 145 +#define LPI_MI2S_TX_4 146 +#define SENARY_MI2S_RX 147 +#define SENARY_MI2S_TX 148 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 From d49ee8faefecd4d2eb5c6c9f419f4db9488d68aa Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:12 +0000 Subject: [PATCH 404/700] ASoC: qcom: common: validate cpu dai id during parsing lpass ports numbers have been added but the afe/apm driver never got updated with new max port value that it uses to store dai specific data. There are more than one places these values are cached and always become out of sync. This will result in array out of bounds and weird driver behaviour. To catch such issues, first add a single place where we can define max port and second add a check in common parsing code which can error out before corrupting the memory with out of bounds array access. This should help both avoid and catch these type of mistakes in future. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-8-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 6 ++++++ sound/soc/qcom/common.h | 3 +++ sound/soc/qcom/lpass.h | 3 ++- sound/soc/qcom/qdsp6/q6afe.h | 3 ++- sound/soc/qcom/qdsp6/q6apm.h | 3 ++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 7ee60a58a3368c..cf1f3a767ceefa 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -120,6 +120,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link->id = args.args[0]; + if (link->id >= LPASS_MAX_PORT) { + dev_err(dev, "%s: Invalid cpu dai id %d\n", link->name, link->id); + ret = -EINVAL; + goto err; + } + if (platform) { link->platforms->of_node = of_parse_phandle(platform, "sound-dai", diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index 1b8d3f90bffaf4..ee6662885593c9 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -4,8 +4,11 @@ #ifndef __QCOM_SND_COMMON_H__ #define __QCOM_SND_COMMON_H__ +#include #include +#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) + int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index de3ec6f594c11c..68b71039b9814e 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -14,10 +14,11 @@ #include #include #include +#include "common.h" #include "lpass-hdmi.h" #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 -#define LPASS_MAX_PORTS (DISPLAY_PORT_RX_7 + 1) +#define LPASS_MAX_PORTS (LPASS_MAX_PORT) #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) #define LPASS_MAX_HDMI_DMA_CHANNELS (4) diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index a29abe4ce43680..0b8c3ec1315caf 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -2,8 +2,9 @@ #ifndef __Q6AFE_H__ #define __Q6AFE_H__ +#include "../common.h" -#define AFE_PORT_MAX 137 +#define AFE_PORT_MAX (LPASS_MAX_PORT) #define MSM_AFE_PORT_TYPE_RX 0 #define MSM_AFE_PORT_TYPE_TX 1 diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7ce08b401e3102..5cf538397841a7 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -14,9 +14,10 @@ #include #include #include +#include "../common.h" #include "audioreach.h" -#define APM_PORT_MAX 127 +#define APM_PORT_MAX LPASS_MAX_PORT #define APM_PORT_MAX_AUDIO_CHAN_CNT 8 #define PCM_CHANNEL_NULL 0 #define PCM_CHANNEL_FL 1 /* Front left channel. */ From bcd0df1ebc9d71e2d53e47fd41ff1482753649b0 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:13 +0000 Subject: [PATCH 405/700] ASoC: qcom: qdsp6: lpass-ports: add support for LPASS LPI MI2S dais Add support for LPASS LPI MI2S dais in the dai-driver, these dais are used in Monaco based platform devices. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-9-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index 4eed54b071a5c4..d31388ed3ccf9b 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -96,6 +96,42 @@ .id = did, \ } +#define Q6AFE_MI2S_RX_DAI(pre, did) { \ + .playback = { \ + .stream_name = pre" MI2S Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .id = did, \ + } + +#define Q6AFE_MI2S_TX_DAI(pre, did) { \ + .capture = { \ + .stream_name = pre" MI2S Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .id = did, \ + } + static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { { .playback = { @@ -484,6 +520,16 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .id = QUINARY_MI2S_TX, .name = "QUIN_MI2S_TX", }, + Q6AFE_MI2S_RX_DAI("LPI RX0", LPI_MI2S_RX_0), + Q6AFE_MI2S_RX_DAI("LPI RX1", LPI_MI2S_RX_1), + Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), + Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), + Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), + Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), + Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), + Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), + Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), + Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), @@ -636,6 +682,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, break; case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; break; case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: From ae0de4e50f8fbd828aff2d79e778ee0e171366ee Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 2 Apr 2026 08:11:14 +0000 Subject: [PATCH 406/700] ASoC: qcom: q6dsp: Add Senary MI2S audio interface support Introduces support for the Senary MI2S audio interface in the Qualcomm q6dsp. Add new AFE port IDs for Senary MI2S RX and TX and include the necessary mappings in the port configuration to allow audio routing over the Senary MI2S interface. Signed-off-by: Mohammad Rafi Shaik Signed-off-by: Srinivas Kandagatla Tested-by: Val Packett # sm7325-motorola-dubai Link: https://patch.msgid.link/20260402081118.348071-10-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-dai.c | 6 +++++ sound/soc/qcom/qdsp6/q6afe.c | 8 +++++++ sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 29 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 0f47aadaabe171..a0d21034a6264d 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -409,6 +409,7 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, q6afe_slim_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].slim); break; + case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], @@ -540,6 +541,7 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"}, {"Quaternary MI2S Playback", NULL, "QUAT_MI2S_RX"}, {"Quinary MI2S Playback", NULL, "QUIN_MI2S_RX"}, + {"Senary MI2S Playback", NULL, "SEN_MI2S_RX"}, {"Primary TDM0 Playback", NULL, "PRIMARY_TDM_RX_0"}, {"Primary TDM1 Playback", NULL, "PRIMARY_TDM_RX_1"}, @@ -636,6 +638,7 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, {"QUIN_MI2S_TX", NULL, "Quinary MI2S Capture"}, + {"SEN_MI2S_TX", NULL, "Senary MI2S Capture"}, {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"}, {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"}, @@ -770,6 +773,8 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_TX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SEN_MI2S_RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEN_MI2S_TX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("QUIN_MI2S_RX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_TX", NULL, @@ -1037,6 +1042,7 @@ static void of_q6afe_parse_dai_data(struct device *dev, switch (id) { /* MI2S specific properties */ + case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: priv = &data->priv[id]; diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 43d877322baee3..40237267fda051 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -130,6 +130,8 @@ #define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007 #define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016 #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 +#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 +#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 /* Start of the range of port IDs for TDM devices. */ #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 @@ -718,6 +720,10 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_MI2S_RX, 1, 1}, [QUINARY_MI2S_TX] = { AFE_PORT_ID_QUINARY_MI2S_TX, QUINARY_MI2S_TX, 0, 1}, + [SENARY_MI2S_RX] = { AFE_PORT_ID_SENARY_MI2S_RX, + SENARY_MI2S_RX, 1, 1}, + [SENARY_MI2S_TX] = { AFE_PORT_ID_SENARY_MI2S_TX, + SENARY_MI2S_TX, 0, 1}, [PRIMARY_TDM_RX_0] = { AFE_PORT_ID_PRIMARY_TDM_RX, PRIMARY_TDM_RX_0, 1, 1}, [PRIMARY_TDM_TX_0] = { AFE_PORT_ID_PRIMARY_TDM_TX, @@ -1777,6 +1783,8 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_QUATERNARY_MI2S_TX: case AFE_PORT_ID_QUINARY_MI2S_RX: case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_RX: + case AFE_PORT_ID_SENARY_MI2S_TX: cfg_type = AFE_PARAM_ID_I2S_CONFIG; break; case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index d31388ed3ccf9b..e5cd82f77b5520 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -519,6 +519,34 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { }, .id = QUINARY_MI2S_TX, .name = "QUIN_MI2S_TX", + }, { + .playback = { + .stream_name = "Senary MI2S Playback", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .id = SENARY_MI2S_RX, + .name = "SEN_MI2S_RX", + }, { + .capture = { + .stream_name = "Senary MI2S Capture", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .id = SENARY_MI2S_TX, + .name = "SEN_MI2S_TX", }, Q6AFE_MI2S_RX_DAI("LPI RX0", LPI_MI2S_RX_0), Q6AFE_MI2S_RX_DAI("LPI RX1", LPI_MI2S_RX_1), @@ -680,6 +708,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, case SLIMBUS_0_RX ... SLIMBUS_6_TX: q6dsp_audio_fe_dais[i].ops = cfg->q6slim_ops; break; + case SENARY_MI2S_RX ... SENARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: From 8f542c7c4aa969331b9be2bad1a1c29883555ab8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:15 +0000 Subject: [PATCH 407/700] ASoC: qcom: qdapm-lpass-dai: correct the error message Fix the error message to reflect the actual graph stop error instead of graph close error. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-11-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index ba64117b8cfe13..87f6db12003e3a 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -150,7 +150,7 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct rc = q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; if (rc < 0) - dev_err(dai->dev, "fail to close APM port (%d)\n", rc); + dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); } if (dai_data->graph[dai->id]) { From b54a38af713830e76f60bab967fd06ee4301eaee Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:16 +0000 Subject: [PATCH 408/700] ASoC: qcom: q6apm-lpass-dai: move graph start to trigger Start the graph at trigger callback. Staring the graph at prepare does not make sense as there is no data transfer at this point. Moving this to trigger will also help cope situation where pipewire is not happy if display port is not connected during start. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-12-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 40 +++++++++++++++++-------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 87f6db12003e3a..e904066484d5cf 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -159,6 +159,31 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } +static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!dai_data->is_port_started[dai->id]) { + ret = q6apm_graph_start(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + else + dai_data->is_port_started[dai->id] = true; + } + break; + default: + break; + } + + return ret; +} + static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -171,10 +196,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; - } } /** @@ -203,14 +224,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } - - rc = q6apm_graph_start(dai_data->graph[dai->id]); - if (rc < 0) { - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - goto err; - } - dai_data->is_port_started[dai->id] = true; - return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -254,6 +267,7 @@ static const struct snd_soc_dai_ops q6dma_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { @@ -263,6 +277,7 @@ static const struct snd_soc_dai_ops q6i2s_ops = { .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -271,6 +286,7 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { From d8b4163038dc02114474d96acefcb48adb4c6e0f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:17 +0000 Subject: [PATCH 409/700] ASoC: qcom: qdsp6: remove search for module iid in hot path Remove searching for Shared Memory module instance id on every read/write call, this is un-necessary if we can cache the shared memory module instance id per PCM graph. Add new member to graph struct to store shared memory module instance id to avoid searching for this in hot path. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-13-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm-dai.c | 5 +++-- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 4 ++-- sound/soc/qcom/qdsp6/q6apm.c | 14 +++++++++----- sound/soc/qcom/qdsp6/q6apm.h | 3 ++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 292be457764f6f..86d6438bd9fd6e 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -355,7 +355,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, spin_lock_init(&prtd->lock); prtd->substream = substream; - prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id, substream->stream); if (IS_ERR(prtd->graph)) { dev_err(dev, "%s: Could not allocate memory\n", __func__); ret = PTR_ERR(prtd->graph); @@ -496,7 +496,8 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component, return -ENOMEM; prtd->cstream = stream; - prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id); + prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id, + SNDRV_PCM_STREAM_PLAYBACK); if (IS_ERR(prtd->graph)) { ret = PTR_ERR(prtd->graph); kfree(prtd); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index e904066484d5cf..006b283484d9e9 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -203,7 +203,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * graph, so sequence for playback and capture will be different */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); rc = PTR_ERR(graph); @@ -240,7 +240,7 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s int graph_id = dai->id; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); return PTR_ERR(graph); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 5751e80b3b9289..6a3942a1ed283f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -411,12 +411,11 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, { struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; struct audio_buffer *ab; - int iid = q6apm_graph_get_rx_shmem_module_iid(graph); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), - graph->port->id, iid); + graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -449,11 +448,10 @@ int q6apm_read(struct q6apm_graph *graph) struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer; struct audioreach_graph_data *port; struct audio_buffer *ab; - int iid = q6apm_graph_get_tx_shmem_module_iid(graph); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, - graph->tx_data.dsp_buf, graph->port->id, iid); + graph->tx_data.dsp_buf, graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -604,7 +602,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) } struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id) + void *priv, int graph_id, int dir) { struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph *ar_graph; @@ -631,6 +629,12 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph->id = ar_graph->id; graph->dev = dev; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + graph->shm_iid = q6apm_graph_get_rx_shmem_module_iid(graph); + else + graph->shm_iid = q6apm_graph_get_tx_shmem_module_iid(graph); + + mutex_init(&graph->lock); init_waitqueue_head(&graph->cmd_wait); diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 5cf538397841a7..7c646ffcf956fd 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -99,6 +99,7 @@ struct q6apm_graph { void *priv; q6apm_cb cb; uint32_t id; + uint32_t shm_iid; struct device *dev; struct q6apm *apm; gpr_port_t *port; @@ -113,7 +114,7 @@ struct q6apm_graph { /* Graph Operations */ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id); + void *priv, int graph_id, int dir); int q6apm_graph_close(struct q6apm_graph *graph); int q6apm_graph_prepare(struct q6apm_graph *graph); int q6apm_graph_start(struct q6apm_graph *graph); From 8ea6e25c8536031604d95dc29a90ef0f114012d7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 2 Apr 2026 08:11:18 +0000 Subject: [PATCH 410/700] ASoC: qcom: q6apm: Add support for early buffer mapping on DSP Buffers are allocated on pcm_new and mapped in the dsp on every prepare call, which is inefficient and unnecessary. Add new functions q6apm_[un]map_memory_fixed_region to map it on to dsp only once after allocation. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260402081118.348071-14-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 60 ------------ sound/soc/qcom/qdsp6/audioreach.h | 5 +- sound/soc/qcom/qdsp6/q6apm-dai.c | 107 ++++++++++++++++++--- sound/soc/qcom/qdsp6/q6apm.c | 151 +++++++++++++++++++----------- sound/soc/qcom/qdsp6/q6apm.h | 14 +-- 5 files changed, 202 insertions(+), 135 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 241c3b4479c6f6..b2975eebab717c 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1396,66 +1396,6 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph) } EXPORT_SYMBOL_GPL(audioreach_graph_free_buf); -int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz, - unsigned int periods, bool is_contiguous) -{ - struct apm_shared_map_region_payload *mregions; - struct apm_cmd_shared_mem_map_regions *cmd; - uint32_t num_regions, buf_sz, payload_size; - struct audioreach_graph_data *data; - struct gpr_pkt *pkt __free(kfree) = NULL; - void *p; - int i; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; - - if (is_contiguous) { - num_regions = 1; - buf_sz = period_sz * periods; - } else { - buf_sz = period_sz; - num_regions = periods; - } - - /* DSP expects size should be aligned to 4K */ - buf_sz = ALIGN(buf_sz, 4096); - - payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions); - - pkt = audioreach_alloc_apm_pkt(payload_size, APM_CMD_SHARED_MEM_MAP_REGIONS, dir, - graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE; - cmd = p; - cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; - cmd->num_regions = num_regions; - - cmd->property_flag = 0x0; - - mregions = p + sizeof(*cmd); - - mutex_lock(&graph->lock); - - for (i = 0; i < num_regions; i++) { - struct audio_buffer *ab; - - ab = &data->buf[i]; - mregions->shm_addr_lsw = lower_32_bits(ab->phys); - mregions->shm_addr_msw = upper_32_bits(ab->phys); - mregions->mem_size_bytes = buf_sz; - ++mregions; - } - mutex_unlock(&graph->lock); - - return audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); -} -EXPORT_SYMBOL_GPL(audioreach_map_memory_regions); - int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) { struct data_cmd_wr_sh_mem_ep_eos *eos; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 89f172aab8c093..6ddc287f0fb4e4 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -722,6 +722,7 @@ struct audioreach_connection { struct audioreach_graph_info { int id; + uint32_t mem_map_handle; uint32_t num_sub_graphs; struct list_head sg_list; /* DPCM connection from FE Graph to BE graph */ @@ -838,10 +839,6 @@ int audioreach_tplg_init(struct snd_soc_component *component); /* Module specific */ void audioreach_graph_free_buf(struct q6apm_graph *graph); -int audioreach_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, size_t period_sz, - unsigned int periods, - bool is_contiguous); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, struct gpr_pkt *pkt, uint32_t rsp_opcode); diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 86d6438bd9fd6e..ede19fdea6e9ee 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -228,11 +228,10 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.bit_width = prtd->bits_per_sample; cfg.fmt = SND_AUDIOCODEC_PCM; audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels); - if (prtd->state) { /* clear the previous setup if any */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } prtd->pcm_count = snd_pcm_lib_period_bytes(substream); @@ -247,8 +246,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, if (ret < 0) dev_err(dev, "%s: CMD Format block failed\n", __func__); - ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys, - (prtd->pcm_size / prtd->periods), prtd->periods); + ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, + (prtd->pcm_size / prtd->periods), prtd->periods); if (ret < 0) { dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); @@ -416,9 +415,10 @@ static int q6apm_dai_close(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; - if (prtd->state) { /* only stop graph that is started */ + if (prtd->state) { + /* only stop graph that is started */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } q6apm_graph_close(prtd->graph); @@ -467,11 +467,94 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, return 0; } +static int q6apm_dai_memory_map(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int graph_id) +{ + struct q6apm_dai_data *pdata; + struct device *dev = component->dev; + phys_addr_t phys; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) { + dev_err(component->dev, "Drv data not found ..\n"); + return -EINVAL; + } + + if (pdata->sid < 0) + phys = substream->dma_buffer.addr; + else + phys = substream->dma_buffer.addr | (pdata->sid << 32); + + ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, BUFFER_BYTES_MAX); + if (ret < 0) + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + + return ret; +} + static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_pcm *pcm = rtd->pcm; int size = BUFFER_BYTES_MAX; + int graph_id, ret; + struct snd_pcm_substream *substream; + + graph_id = cpu_dai->driver->id; + + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + if (ret) + return ret; + + /* Note: DSP backend dais are uni-directional ONLY(either playback or capture) */ + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + ret = q6apm_dai_memory_map(component, substream, graph_id); + if (ret) + return ret; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ret = q6apm_dai_memory_map(component, substream, graph_id); + if (ret) + return ret; + } + + return 0; +} + +static void q6apm_dai_memory_unmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_prtd; + struct snd_soc_dai *cpu_dai; + int graph_id; + + soc_prtd = snd_soc_substream_to_rtd(substream); + if (!soc_prtd) + return; + + cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); + if (!cpu_dai) + return; + + graph_id = cpu_dai->driver->id; + q6apm_unmap_memory_fixed_region(component->dev, graph_id); +} + +static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) + q6apm_dai_memory_unmap(component, substream); - return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) + q6apm_dai_memory_unmap(component, substream); } static int q6apm_dai_compr_open(struct snd_soc_component *component, @@ -530,7 +613,8 @@ static int q6apm_dai_compr_free(struct snd_soc_component *component, struct q6apm_dai_rtd *prtd = runtime->private_data; q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_free_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_unmap_memory_fixed_region(component->dev, prtd->graph->id); q6apm_graph_close(prtd->graph); snd_dma_free_pages(&prtd->dma_buffer); prtd->graph = NULL; @@ -679,9 +763,9 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component, if (ret) return ret; - ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, - prtd->phys, (prtd->pcm_size / prtd->periods), - prtd->periods); + ret = q6apm_alloc_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, + prtd->phys, (prtd->pcm_size / prtd->periods), + prtd->periods); if (ret < 0) return -ENOMEM; @@ -834,6 +918,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .close = q6apm_dai_close, .prepare = q6apm_dai_prepare, .pcm_new = q6apm_dai_pcm_new, + .pcm_free = q6apm_dai_pcm_free, .hw_params = q6apm_dai_hw_params, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 6a3942a1ed283f..3c119a6132e482 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -200,13 +200,53 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, } EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem); -int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods) +int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phys_addr_t phys, + size_t sz) +{ + struct audioreach_graph_info *info; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct apm_shared_map_region_payload *mregions; + struct apm_cmd_shared_mem_map_regions *cmd; + int payload_size = sizeof(*cmd) + (sizeof(*mregions)); + uint32_t buf_sz; + void *p; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, + APM_CMD_SHARED_MEM_MAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; + + if (info->mem_map_handle) + return 0; + + /* DSP expects size should be aligned to 4K */ + buf_sz = ALIGN(sz, 4096); + + p = (void *)pkt + GPR_HDR_SIZE; + cmd = p; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = 1; + cmd->property_flag = 0x0; + + mregions = p + sizeof(*cmd); + + mregions->shm_addr_lsw = lower_32_bits(phys); + mregions->shm_addr_msw = upper_32_bits(phys); + mregions->mem_size_bytes = buf_sz; + + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_map_memory_fixed_region); + +int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, + size_t period_sz, unsigned int periods) { struct audioreach_graph_data *data; struct audio_buffer *buf; int cnt; - int rc; if (dir == SNDRV_PCM_STREAM_PLAYBACK) data = &graph->rx_data; @@ -248,46 +288,41 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a mutex_unlock(&graph->lock); - rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1); - if (rc < 0) { - dev_err(graph->dev, "Memory_map_regions failed\n"); - audioreach_graph_free_buf(graph); - } - - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_map_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) +int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct audioreach_graph_data *data; - int rc; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph_info *info; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), + APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; - if (!data->mem_map_handle) + if (!info->mem_map_handle) return 0; - struct gpr_pkt *pkt __free(kfree) = - audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, - dir, graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - cmd = (void *)pkt + GPR_HDR_SIZE; - cmd->mem_map_handle = data->mem_map_handle; + cmd->mem_map_handle = info->mem_map_handle; - rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); +int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir) +{ audioreach_graph_free_buf(graph); - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_free_fragments); int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) { @@ -429,7 +464,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, write_buffer->buf_size = len; write_buffer->timestamp_lsw = lsw_ts; write_buffer->timestamp_msw = msw_ts; - write_buffer->mem_map_handle = graph->rx_data.mem_map_handle; + write_buffer->mem_map_handle = graph->info->mem_map_handle; write_buffer->flags = wflags; graph->rx_data.dsp_buf++; @@ -463,7 +498,7 @@ int q6apm_read(struct q6apm_graph *graph) read_buffer->buf_addr_lsw = lower_32_bits(ab->phys); read_buffer->buf_addr_msw = upper_32_bits(ab->phys); - read_buffer->mem_map_handle = port->mem_map_handle; + read_buffer->mem_map_handle = graph->info->mem_map_handle; read_buffer->buf_size = ab->size; port->dsp_buf++; @@ -494,7 +529,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done; - struct apm_cmd_rsp_shared_mem_map_regions *rsp; const struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; const struct gpr_hdr *hdr = &data->hdr; @@ -529,18 +563,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) done->buf_addr_msw); } - break; - case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: - graph->result.opcode = hdr->opcode; - graph->result.status = 0; - rsp = data->payload; - - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = rsp->mem_map_handle; - else - graph->tx_data.mem_map_handle = rsp->mem_map_handle; - - wake_up(&graph->cmd_wait); break; case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2: if (!graph->ar_graph) @@ -571,16 +593,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { - case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - graph->result.opcode = result->opcode; - graph->result.status = 0; - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = 0; - else - graph->tx_data.mem_map_handle = 0; - - wake_up(&graph->cmd_wait); - break; case APM_CMD_SHARED_MEM_MAP_REGIONS: case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT: case APM_CMD_SET_CFG: @@ -784,7 +796,9 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; + struct audioreach_graph_info *info; struct q6apm *apm = dev_get_drvdata(&gdev->dev); + struct apm_cmd_rsp_shared_mem_map_regions *rsp; struct device *dev = &gdev->dev; struct gpr_ibasic_rsp_result_t *result; const struct gpr_hdr *hdr = &data->hdr; @@ -801,6 +815,7 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { + case APM_CMD_SHARED_MEM_MAP_REGIONS: case APM_CMD_GRAPH_START: case APM_CMD_GRAPH_OPEN: case APM_CMD_GRAPH_PREPARE: @@ -815,10 +830,38 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) result->opcode); wake_up(&apm->wait); break; + case APM_CMD_SHARED_MEM_UNMAP_REGIONS: + apm->result.opcode = hdr->opcode; + apm->result.status = 0; + rsp = data->payload; + + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info) + info->mem_map_handle = 0; + else + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } break; + case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: + apm->result.opcode = hdr->opcode; + apm->result.status = 0; + rsp = data->payload; + + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info) + info->mem_map_handle = rsp->mem_map_handle; + else + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7c646ffcf956fd..909fc337fd2840 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -78,7 +78,6 @@ struct audioreach_graph_data { struct audio_buffer *buf; uint32_t num_periods; uint32_t dsp_buf; - uint32_t mem_map_handle; atomic_t hw_ptr; }; @@ -134,11 +133,14 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); /* Memory Map related */ -int q6apm_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, - unsigned int dir); +int q6apm_map_memory_fixed_region(struct device *dev, + unsigned int graph_id, phys_addr_t phys, + size_t sz); +int q6apm_alloc_fragments(struct q6apm_graph *graph, + unsigned int dir, phys_addr_t phys, + size_t period_sz, unsigned int periods); +int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir); +int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id); /* Helpers */ int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode); From ec7067e661193403a7a00980bda8612db5954142 Mon Sep 17 00:00:00 2001 From: Dimitri Daskalakis Date: Wed, 1 Apr 2026 09:28:48 -0700 Subject: [PATCH 411/700] eth: fbnic: Increase FBNIC_QUEUE_SIZE_MIN to 64 On systems with 64K pages, RX queues will be wedged if users set the descriptor count to the current minimum (16). Fbnic fragments large pages into 4K chunks, and scales down the ring size accordingly. With 64K pages and 16 descriptors, the ring size mask is 0 and will never be filled. 32 descriptors is another special case that wedges the RX rings. Internally, the rings track pages for the head/tail pointers, not page fragments. So with 32 descriptors, there's only 1 usable page as one ring slot is kept empty to disambiguate between an empty/full ring. As a result, the head pointer never advances and the HW stalls after consuming 16 page fragments. Fixes: 0cb4c0a13723 ("eth: fbnic: Implement Rx queue alloc/start/stop/free") Signed-off-by: Dimitri Daskalakis Link: https://patch.msgid.link/20260401162848.2335350-1-dimitri.daskalakis1@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h index 98096527407955..e03c9d2c38dca1 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h @@ -38,7 +38,7 @@ struct fbnic_net; #define FBNIC_MAX_XDPQS 128u /* These apply to TWQs, TCQ, RCQ */ -#define FBNIC_QUEUE_SIZE_MIN 16u +#define FBNIC_QUEUE_SIZE_MIN 64u #define FBNIC_QUEUE_SIZE_MAX SZ_64K #define FBNIC_TXQ_SIZE_DEFAULT 1024 From b0db1accbc7395657c2b79db59fa9fae0d6656f3 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Thu, 2 Apr 2026 17:29:22 +0800 Subject: [PATCH 412/700] bpf: reject direct access to nullable PTR_TO_BUF pointers check_mem_access() matches PTR_TO_BUF via base_type() which strips PTR_MAYBE_NULL, allowing direct dereference without a null check. Map iterator ctx->key and ctx->value are PTR_TO_BUF | PTR_MAYBE_NULL. On stop callbacks these are NULL, causing a kernel NULL dereference. Add a type_may_be_null() guard to the PTR_TO_BUF branch, matching the existing PTR_TO_BTF_ID pattern. Fixes: 20b2aff4bc15 ("bpf: Introduce MEM_RDONLY flag") Signed-off-by: Qi Tang Acked-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260402092923.38357-2-tpluszz77@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a3388cb8fcbdf9..df04dccfc54097 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7905,7 +7905,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } else if (reg->type == CONST_PTR_TO_MAP) { err = check_ptr_to_map_access(env, regs, regno, off, size, t, value_regno); - } else if (base_type(reg->type) == PTR_TO_BUF) { + } else if (base_type(reg->type) == PTR_TO_BUF && + !type_may_be_null(reg->type)) { bool rdonly_mem = type_is_rdonly_mem(reg->type); u32 *max_access; From eb7024bfcc5f68ed11ed9dd4891a3073c15f04a8 Mon Sep 17 00:00:00 2001 From: Varun R Mallya Date: Thu, 2 Apr 2026 00:41:25 +0530 Subject: [PATCH 413/700] bpf: Reject sleepable kprobe_multi programs at attach time kprobe.multi programs run in atomic/RCU context and cannot sleep. However, bpf_kprobe_multi_link_attach() did not validate whether the program being attached had the sleepable flag set, allowing sleepable helpers such as bpf_copy_from_user() to be invoked from a non-sleepable context. This causes a "sleeping function called from invalid context" splat: BUG: sleeping function called from invalid context at ./include/linux/uaccess.h:169 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1787, name: sudo preempt_count: 1, expected: 0 RCU nest depth: 2, expected: 0 Fix this by rejecting sleepable programs early in bpf_kprobe_multi_link_attach(), before any further processing. Fixes: 0dcac2725406 ("bpf: Add multi kprobe link") Signed-off-by: Varun R Mallya Acked-by: Kumar Kartikeya Dwivedi Acked-by: Leon Hwang Acked-by: Jiri Olsa Link: https://lore.kernel.org/r/20260401191126.440683-1-varunrmallya@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/trace/bpf_trace.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 0b040a4174428f..af7079aa0f36d9 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2752,6 +2752,10 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr if (!is_kprobe_multi(prog)) return -EINVAL; + /* kprobe_multi is not allowed to be sleepable. */ + if (prog->sleepable) + return -EINVAL; + /* Writing to context is not allowed for kprobes. */ if (prog->aux->kprobe_write_ctx) return -EINVAL; From 179ee84a89114b854ac2dd1d293633a7f6c8dac1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 1 Apr 2026 00:20:19 +0200 Subject: [PATCH 414/700] bpf: Fix incorrect pruning due to atomic fetch precision tracking When backtrack_insn encounters a BPF_STX instruction with BPF_ATOMIC and BPF_FETCH, the src register (or r0 for BPF_CMPXCHG) also acts as a destination, thus receiving the old value from the memory location. The current backtracking logic does not account for this. It treats atomic fetch operations the same as regular stores where the src register is only an input. This leads the backtrack_insn to fail to propagate precision to the stack location, which is then not marked as precise! Later, the verifier's path pruning can incorrectly consider two states equivalent when they differ in terms of stack state. Meaning, two branches can be treated as equivalent and thus get pruned when they should not be seen as such. Fix it as follows: Extend the BPF_LDX handling in backtrack_insn to also cover atomic fetch operations via is_atomic_fetch_insn() helper. When the fetch dst register is being tracked for precision, clear it, and propagate precision over to the stack slot. For non-stack memory, the precision walk stops at the atomic instruction, same as regular BPF_LDX. This covers all fetch variants. Before: 0: (b7) r1 = 8 ; R1=8 1: (7b) *(u64 *)(r10 -8) = r1 ; R1=8 R10=fp0 fp-8=8 2: (b7) r2 = 0 ; R2=0 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) ; R2=8 R10=fp0 fp-8=mmmmmmmm 4: (bf) r3 = r10 ; R3=fp0 R10=fp0 5: (0f) r3 += r2 mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1 mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10 mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) mark_precise: frame0: regs=r2 stack= before 2: (b7) r2 = 0 6: R2=8 R3=fp8 6: (b7) r0 = 0 ; R0=0 7: (95) exit After: 0: (b7) r1 = 8 ; R1=8 1: (7b) *(u64 *)(r10 -8) = r1 ; R1=8 R10=fp0 fp-8=8 2: (b7) r2 = 0 ; R2=0 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) ; R2=8 R10=fp0 fp-8=mmmmmmmm 4: (bf) r3 = r10 ; R3=fp0 R10=fp0 5: (0f) r3 += r2 mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1 mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10 mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2) mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0 mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1 mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8 6: R2=8 R3=fp8 6: (b7) r0 = 0 ; R0=0 7: (95) exit Fixes: 5ffa25502b5a ("bpf: Add instructions for atomic_[cmp]xchg") Fixes: 5ca419f2864a ("bpf: Add BPF_FETCH field / create atomic_fetch_add instruction") Reported-by: STAR Labs SG Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260331222020.401848-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index df04dccfc54097..e3814152b52f81 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -617,6 +617,13 @@ static bool is_atomic_load_insn(const struct bpf_insn *insn) insn->imm == BPF_LOAD_ACQ; } +static bool is_atomic_fetch_insn(const struct bpf_insn *insn) +{ + return BPF_CLASS(insn->code) == BPF_STX && + BPF_MODE(insn->code) == BPF_ATOMIC && + (insn->imm & BPF_FETCH); +} + static int __get_spi(s32 off) { return (-off - 1) / BPF_REG_SIZE; @@ -4447,10 +4454,24 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, * dreg still needs precision before this insn */ } - } else if (class == BPF_LDX || is_atomic_load_insn(insn)) { - if (!bt_is_reg_set(bt, dreg)) + } else if (class == BPF_LDX || + is_atomic_load_insn(insn) || + is_atomic_fetch_insn(insn)) { + u32 load_reg = dreg; + + /* + * Atomic fetch operation writes the old value into + * a register (sreg or r0) and if it was tracked for + * precision, propagate to the stack slot like we do + * in regular ldx. + */ + if (is_atomic_fetch_insn(insn)) + load_reg = insn->imm == BPF_CMPXCHG ? + BPF_REG_0 : sreg; + + if (!bt_is_reg_set(bt, load_reg)) return 0; - bt_clear_reg(bt, dreg); + bt_clear_reg(bt, load_reg); /* scalars can only be spilled into stack w/o losing precision. * Load from any other memory can be zero extended. From e1b5687a862a43429f1d9f69065b3bbc7780a97a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 1 Apr 2026 00:20:20 +0200 Subject: [PATCH 415/700] selftests/bpf: Add more precision tracking tests for atomics Add verifier precision tracking tests for BPF atomic fetch operations. Validate that backtrack_insn correctly propagates precision from the fetch dst_reg to the stack slot for {fetch_add,xchg,cmpxchg} atomics. For the first two src_reg gets the old memory value, and for the last one r0. The fetched register is used for pointer arithmetic to trigger backtracking. Also add coverage for fetch_{or,and,xor} flavors which exercises the bitwise atomic fetch variants going through the same insn->imm & BPF_FETCH check but with different imm values. Add dual-precision regression tests for fetch_add and cmpxchg where both the fetched value and a reread of the same stack slot are tracked for precision. After the atomic operation, the stack slot is STACK_MISC, so the ldx does not set INSN_F_STACK_ACCESS. These tests verify that stack precision propagates solely through the atomic fetch's load side. Add map-based tests for fetch_add and cmpxchg which validate that non- stack atomic fetch completes precision tracking without falling back to mark_all_scalars_precise. Lastly, add 32-bit variants for {fetch_add, cmpxchg} on map values to cover the second valid atomic operand size. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_precision [...] + /etc/rcS.d/S50-startup ./test_progs -t verifier_precision [ 1.697105] bpf_testmod: loading out-of-tree module taints kernel. [ 1.700220] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel [ 1.777043] tsc: Refined TSC clocksource calibration: 3407.986 MHz [ 1.777619] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fc6d7268, max_idle_ns: 440795260133 ns [ 1.778658] clocksource: Switched to clocksource tsc #633/1 verifier_precision/bpf_neg:OK #633/2 verifier_precision/bpf_end_to_le:OK #633/3 verifier_precision/bpf_end_to_be:OK #633/4 verifier_precision/bpf_end_bswap:OK #633/5 verifier_precision/bpf_load_acquire:OK #633/6 verifier_precision/bpf_store_release:OK #633/7 verifier_precision/state_loop_first_last_equal:OK #633/8 verifier_precision/bpf_cond_op_r10:OK #633/9 verifier_precision/bpf_cond_op_not_r10:OK #633/10 verifier_precision/bpf_atomic_fetch_add_precision:OK #633/11 verifier_precision/bpf_atomic_xchg_precision:OK #633/12 verifier_precision/bpf_atomic_fetch_or_precision:OK #633/13 verifier_precision/bpf_atomic_fetch_and_precision:OK #633/14 verifier_precision/bpf_atomic_fetch_xor_precision:OK #633/15 verifier_precision/bpf_atomic_cmpxchg_precision:OK #633/16 verifier_precision/bpf_atomic_fetch_add_dual_precision:OK #633/17 verifier_precision/bpf_atomic_cmpxchg_dual_precision:OK #633/18 verifier_precision/bpf_atomic_fetch_add_map_precision:OK #633/19 verifier_precision/bpf_atomic_cmpxchg_map_precision:OK #633/20 verifier_precision/bpf_atomic_fetch_add_32bit_precision:OK #633/21 verifier_precision/bpf_atomic_cmpxchg_32bit_precision:OK #633/22 verifier_precision/bpf_neg_2:OK #633/23 verifier_precision/bpf_neg_3:OK #633/24 verifier_precision/bpf_neg_4:OK #633/25 verifier_precision/bpf_neg_5:OK #633 verifier_precision:OK Summary: 1/25 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260331222020.401848-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/verifier_precision.c | 341 ++++++++++++++++++ 1 file changed, 341 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 1fe090cd674495..4794903aec8e63 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -5,6 +5,13 @@ #include "../../../include/linux/filter.h" #include "bpf_misc.h" +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} precision_map SEC(".maps"); + SEC("?raw_tp") __success __log_level(2) __msg("mark_precise: frame0: regs=r2 stack= before 3: (bf) r1 = r10") @@ -301,4 +308,338 @@ __naked int bpf_neg_5(void) ::: __clobber_all); } +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_add_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_add_insn];" /* r2 = atomic_fetch_add(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_xchg((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_xchg_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[xchg_insn];" /* r2 = atomic_xchg(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(xchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_or((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_or_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_or_insn];" /* r2 = atomic_fetch_or(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_or_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_and((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_and_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_and_insn];" /* r2 = atomic_fetch_and(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_and_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_AND | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2 stack= before 4: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_xor((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_xor_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_xor_insn];" /* r2 = atomic_fetch_xor(*(u64 *)(r10 - 8), r2) */ + "r3 = r10;" + "r3 += r2;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_xor_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_XOR | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 5: (bf) r3 = r10") +__msg("mark_precise: frame0: regs=r0 stack= before 4: (db) r0 = atomic64_cmpxchg((u64 *)(r10 -8), r0, r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 3: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r0 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_cmpxchg_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r0 = 0;" + "r2 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r10 - 8), r0, r2) */ + "r3 = r10;" + "r3 += r0;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +/* Regression test for dual precision: Both the fetched value (r2) and + * a reread of the same stack slot (r3) are tracked for precision. After + * the atomic operation, the stack slot is STACK_MISC. Thus, the ldx at + * insn 4 does NOT set INSN_F_STACK_ACCESS. Precision for the stack slot + * propagates solely through the atomic fetch's load side (insn 3). + */ +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r2,r3 stack= before 4: (79) r3 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs=r2 stack= before 3: (db) r2 = atomic64_fetch_add((u64 *)(r10 -8), r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_fetch_add_dual_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = 0;" + ".8byte %[fetch_add_insn];" /* r2 = atomic_fetch_add(*(u64 *)(r10 - 8), r2) */ + "r3 = *(u64 *)(r10 - 8);" + "r4 = r2;" + "r4 += r3;" + "r4 &= 7;" + "r5 = r10;" + "r5 += r4;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0,r3 stack= before 5: (79) r3 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs=r0 stack= before 4: (db) r0 = atomic64_cmpxchg((u64 *)(r10 -8), r0, r2)") +__msg("mark_precise: frame0: regs= stack=-8 before 3: (b7) r2 = 0") +__msg("mark_precise: frame0: regs= stack=-8 before 2: (b7) r0 = 8") +__msg("mark_precise: frame0: regs= stack=-8 before 1: (7b) *(u64 *)(r10 -8) = r1") +__msg("mark_precise: frame0: regs=r1 stack= before 0: (b7) r1 = 8") +__naked int bpf_atomic_cmpxchg_dual_precision(void) +{ + asm volatile ( + "r1 = 8;" + "*(u64 *)(r10 - 8) = r1;" + "r0 = 8;" + "r2 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r10 - 8), r0, r2) */ + "r3 = *(u64 *)(r10 - 8);" + "r4 = r0;" + "r4 += r3;" + "r4 &= 7;" + "r5 = r10;" + "r5 += r4;" /* mark_precise */ + "r0 = 0;" + "exit;" + : + : __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r1 stack= before 10: (57) r1 &= 7") +__msg("mark_precise: frame0: regs=r1 stack= before 9: (db) r1 = atomic64_fetch_add((u64 *)(r0 +0), r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_fetch_add_map_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r1 = 0;" + ".8byte %[fetch_add_insn];" /* r1 = atomic_fetch_add(*(u64 *)(r0 + 0), r1) */ + "r1 &= 7;" + "r2 = r10;" + "r2 += r1;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_0, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 12: (57) r0 &= 7") +__msg("mark_precise: frame0: regs=r0 stack= before 11: (db) r0 = atomic64_cmpxchg((u64 *)(r6 +0), r0, r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_cmpxchg_map_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r6 = r0;" + "r0 = 0;" + "r1 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u64 *)(r6 + 0), r0, r1) */ + "r0 &= 7;" + "r2 = r10;" + "r2 += r0;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_6, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r1 stack= before 10: (57) r1 &= 7") +__msg("mark_precise: frame0: regs=r1 stack= before 9: (c3) r1 = atomic_fetch_add((u32 *)(r0 +0), r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_fetch_add_32bit_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r1 = 0;" + ".8byte %[fetch_add_insn];" /* r1 = atomic_fetch_add(*(u32 *)(r0 + 0), r1) */ + "r1 &= 7;" + "r2 = r10;" + "r2 += r1;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(fetch_add_insn, + BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_0, BPF_REG_1, 0)) + : __clobber_all); +} + +SEC("?raw_tp") +__success __log_level(2) +__msg("mark_precise: frame0: regs=r0 stack= before 12: (57) r0 &= 7") +__msg("mark_precise: frame0: regs=r0 stack= before 11: (c3) r0 = atomic_cmpxchg((u32 *)(r6 +0), r0, r1)") +__not_msg("falling back to forcing all scalars precise") +__naked int bpf_atomic_cmpxchg_32bit_precision(void) +{ + asm volatile ( + "r1 = 0;" + "*(u64 *)(r10 - 8) = r1;" + "r2 = r10;" + "r2 += -8;" + "r1 = %[precision_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + "r6 = r0;" + "r0 = 0;" + "r1 = 0;" + ".8byte %[cmpxchg_insn];" /* r0 = atomic_cmpxchg(*(u32 *)(r6 + 0), r0, r1) */ + "r0 &= 7;" + "r2 = r10;" + "r2 += r0;" /* mark_precise */ + "1: r0 = 0;" + "exit;" + : + : __imm_addr(precision_map), + __imm(bpf_map_lookup_elem), + __imm_insn(cmpxchg_insn, + BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_6, BPF_REG_1, 0)) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From a4983968fa5b3179ab090407d325a71cdc96874e Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Mon, 23 Mar 2026 23:13:43 +0200 Subject: [PATCH 416/700] drm/amd/display: Wire up dcn10_dio_construct() for all pre-DCN401 generations Description: - Commit b82f0759346617b2 ("drm/amd/display: Migrate DIO registers access from hwseq to dio component") moved DIO_MEM_PWR_CTRL register access behind the new dio abstraction layer but only created the dio object for DCN 4.01. On all other generations (DCN 10/20/21/201/30/301/302/303/ 31/314/315/316/32/321/35/351/36), the dio pointer is NULL, causing the register write to be silently skipped. This results in AFMT HDMI memory not being powered on during init_hw, which can cause HDMI audio failures and display issues on affected hardware including Renoir/Cezanne (DCN 2.1) APUs that use dcn10_init_hw. Call dcn10_dio_construct() in each older DCN generation's resource.c to create the dio object, following the same pattern as DCN 4.01. This ensures the dio pointer is non-NULL and the mem_pwr_ctrl callback works through the dio abstraction for all DCN generations. Fixes: b82f07593466 ("drm/amd/display: Migrate DIO registers access from hwseq to dio component.") Reviewed-by: Ivan Lipski Signed-off-by: Ionut Nechita Signed-off-by: Alex Deucher --- .../dc/resource/dcn10/dcn10_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn20/dcn20_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn201/dcn201_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn21/dcn21_resource.c | 34 +++++++++++++++ .../dc/resource/dcn30/dcn30_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn301/dcn301_resource.c | 42 ++++++++++++++++++ .../dc/resource/dcn302/dcn302_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn303/dcn303_resource.c | 41 ++++++++++++++++++ .../dc/resource/dcn31/dcn31_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn314/dcn314_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn315/dcn315_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn316/dcn316_resource.c | 40 +++++++++++++++++ .../dc/resource/dcn32/dcn32_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn321/dcn321_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn35/dcn35_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn351/dcn351_resource.c | 43 +++++++++++++++++++ .../dc/resource/dcn36/dcn36_resource.c | 43 +++++++++++++++++++ 17 files changed, 699 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c index bbe185e15eb679..4663456a736a2f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c @@ -71,6 +71,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #ifndef mmDP0_DP_DPHY_INTERNAL_CTRL #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f @@ -444,6 +445,33 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN10(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn10_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static int map_transmitter_id_to_phy_instance( enum transmitter transmitter) { @@ -917,6 +945,11 @@ static void dcn10_resource_destruct(struct dcn10_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.opps[i] != NULL) pool->base.opps[i]->funcs->opp_destroy(&pool->base.opps[i]); @@ -1653,6 +1686,14 @@ static bool dcn10_resource_construct( goto fail; } + /* DIO */ + pool->base.dio = dcn10_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto fail; + } + if (!resource_construct(num_virtual_links, dc, &pool->base, &res_create_funcs)) goto fail; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c index 8b555187ac7537..74e8d229c9dd3c 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c @@ -82,6 +82,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #include "vm_helper.h" #include "link_enc_cfg.h" @@ -550,6 +551,33 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN20(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn20_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + #define vmid_regs(id)\ [id] = {\ DCN20_VMID_REG_LIST(id)\ @@ -1104,6 +1132,12 @@ static void dcn20_resource_destruct(struct dcn20_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn20_dpp_destroy(&pool->base.dpps[i]); @@ -2692,6 +2726,14 @@ static bool dcn20_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn20_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn20_dsc_create(ctx, i); if (pool->base.dscs[i] == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c index 4ea76e46ab15de..e289be70efb54c 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c @@ -56,6 +56,7 @@ #include "dce/dce_aux.h" #include "dce/dce_i2c.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "cyan_skillfish_ip_offset.h" @@ -755,6 +756,33 @@ static struct hubbub *dcn201_hubbub_create(struct dc_context *ctx) return &hubbub->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn201_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn201_timing_generator_create( struct dc_context *ctx, uint32_t instance) @@ -930,6 +958,11 @@ static void dcn201_resource_destruct(struct dcn201_resource_pool *pool) pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn201_dpp_destroy(&pool->base.dpps[i]); @@ -1276,6 +1309,14 @@ static bool dcn201_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn201_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + if (!resource_construct(num_virtual_links, dc, &pool->base, &res_create_funcs)) goto create_fail; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index 0f4307f8f3dd59..4333baac96ad7d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -84,6 +84,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_i2c.h" +#include "dio/dcn10/dcn10_dio.h" #include "dcn21_resource.h" #include "vm_helper.h" #include "dcn20/dcn20_vmid.h" @@ -329,6 +330,25 @@ static const struct dcn_hubbub_mask hubbub_mask = { HUBBUB_MASK_SH_LIST_DCN21(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +static const struct dcn_dio_shift dio_shift = { 0 }; + +static const struct dcn_dio_mask dio_mask = { 0 }; + +static struct dio *dcn21_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} #define vmid_regs(id)\ [id] = {\ @@ -677,6 +697,12 @@ static void dcn21_resource_destruct(struct dcn21_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn20_dpp_destroy(&pool->base.dpps[i]); @@ -1654,6 +1680,14 @@ static bool dcn21_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn21_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + for (i = 0; i < pool->base.res_cap->num_dsc; i++) { pool->base.dscs[i] = dcn21_dsc_create(ctx, i); if (pool->base.dscs[i] == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 2fa86b9587ed00..87b7b4ee04c647 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -60,6 +60,7 @@ #include "dml/display_mode_vba.h" #include "dcn30/dcn30_dccg.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" #include "dce/dce_panel_cntl.h" @@ -886,6 +887,33 @@ static struct hubbub *dcn30_hubbub_create(struct dc_context *ctx) return &hubbub3->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn30_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn30_timing_generator_create( struct dc_context *ctx, uint32_t instance) @@ -1095,6 +1123,12 @@ static void dcn30_resource_destruct(struct dcn30_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn30_dpp_destroy(&pool->base.dpps[i]); @@ -2464,6 +2498,14 @@ static bool dcn30_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn30_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn30_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index 7842bee57e636c..6bb1c62124bb46 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -59,6 +59,7 @@ #include "dml/display_mode_vba.h" #include "dcn301/dcn301_dccg.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn301/dcn301_dio_link_encoder.h" #include "dcn301/dcn301_panel_cntl.h" @@ -843,6 +844,33 @@ static struct hubbub *dcn301_hubbub_create(struct dc_context *ctx) return &hubbub3->base; } +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn301_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct timing_generator *dcn301_timing_generator_create( struct dc_context *ctx, uint32_t instance) { @@ -1066,6 +1094,12 @@ static void dcn301_destruct(struct dcn301_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn301_dpp_destroy(&pool->base.dpps[i]); @@ -1582,6 +1616,14 @@ static bool dcn301_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn301_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + j = 0; /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 1874d5d6b78200..d02aafd06fd455 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -46,6 +46,7 @@ #include "dml/dcn30/dcn30_fpu.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" @@ -253,6 +254,33 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn302_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn302_hubbub_create(struct dc_context *ctx) { int i; @@ -1022,6 +1050,11 @@ static void dcn302_resource_destruct(struct resource_pool *pool) pool->hubbub = NULL; } + if (pool->dio != NULL) { + kfree(TO_DCN10_DIO(pool->dio)); + pool->dio = NULL; + } + for (i = 0; i < pool->pipe_count; i++) { if (pool->dpps[i] != NULL) { kfree(TO_DCN20_DPP(pool->dpps[i])); @@ -1372,6 +1405,14 @@ static bool dcn302_resource_construct( goto create_fail; } + /* DIO */ + pool->dio = dcn302_dio_create(ctx); + if (pool->dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->pipe_count; i++) { pool->hubps[i] = dcn302_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index d52201cb359fd5..30b1403112c6c1 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -46,6 +46,7 @@ #include "dml/dcn30/dcn30_fpu.h" #include "dcn10/dcn10_resource.h" +#include "dio/dcn10/dcn10_dio.h" #include "link_service.h" @@ -249,6 +250,33 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + +static struct dio *dcn303_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn303_hubbub_create(struct dc_context *ctx) { int i; @@ -966,6 +994,11 @@ static void dcn303_resource_destruct(struct resource_pool *pool) pool->hubbub = NULL; } + if (pool->dio != NULL) { + kfree(TO_DCN10_DIO(pool->dio)); + pool->dio = NULL; + } + for (i = 0; i < pool->pipe_count; i++) { if (pool->dpps[i] != NULL) { kfree(TO_DCN20_DPP(pool->dpps[i])); @@ -1304,6 +1337,14 @@ static bool dcn303_resource_construct( goto create_fail; } + /* DIO */ + pool->dio = dcn303_dio_create(ctx); + if (pool->dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->pipe_count; i++) { pool->hubps[i] = dcn303_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 2055f1f8af6521..4e9c041c707a61 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -64,6 +64,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -810,6 +811,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1021,6 +1037,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn31_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1396,6 +1424,10 @@ static void dcn31_resource_destruct(struct dcn31_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -2063,6 +2095,14 @@ static bool dcn31_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn31_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 1939f720ba2957..e26a6427916a0b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -66,6 +66,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -822,6 +823,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn314 = { .num_timing_generator = 4, .num_opp = 4, @@ -1079,6 +1095,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn314_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1455,6 +1483,10 @@ static void dcn314_resource_destruct(struct dcn314_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -1987,6 +2019,14 @@ static bool dcn314_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn314_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index e8377c190f635e..131a6cd4c73528 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -63,6 +63,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -809,6 +810,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1020,6 +1036,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn315_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1397,6 +1425,10 @@ static void dcn315_resource_destruct(struct dcn315_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -2012,6 +2044,14 @@ static bool dcn315_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn315_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 045ce01bd74eb1..c8c0ce6efcfdcf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -63,6 +63,7 @@ #include "dce/dce_audio.h" #include "dce/dce_hwseq.h" #include "clk_mgr.h" +#include "dio/dcn10/dcn10_dio.h" #include "dio/virtual/virtual_stream_encoder.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" @@ -804,6 +805,21 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static const struct dcn_dio_registers dio_regs = { + DIO_REG_LIST_DCN10() +}; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn31 = { .num_timing_generator = 4, .num_opp = 4, @@ -1013,6 +1029,18 @@ static struct mpc *dcn31_mpc_create( return &mpc30->base; } +static struct dio *dcn316_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn31_hubbub_create(struct dc_context *ctx) { int i; @@ -1392,6 +1420,10 @@ static void dcn316_resource_destruct(struct dcn316_resource_pool *pool) kfree(pool->base.hubbub); pool->base.hubbub = NULL; } + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } for (i = 0; i < pool->base.pipe_count; i++) { if (pool->base.dpps[i] != NULL) dcn31_dpp_destroy(&pool->base.dpps[i]); @@ -1887,6 +1919,14 @@ static bool dcn316_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn316_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn31_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index c7fd604024d645..c3a6ae14de18bf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -66,6 +66,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dml/display_mode_vba.h" #include "dcn32/dcn32_dccg.h" #include "dcn10/dcn10_resource.h" @@ -643,6 +644,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn32 = { .num_timing_generator = 4, .num_opp = 4, @@ -833,6 +847,22 @@ static struct clock_source *dcn32_clock_source_create( return NULL; } +static struct dio *dcn32_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn32_hubbub_create(struct dc_context *ctx) { int i; @@ -1494,6 +1524,11 @@ static void dcn32_resource_destruct(struct dcn32_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + if (pool->base.oem_device != NULL) { struct dc *dc = pool->base.oem_device->ctx->dc; @@ -2373,6 +2408,14 @@ static bool dcn32_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn32_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs, TGs, ABMs */ for (i = 0, j = 0; i < pool->base.res_cap->num_timing_generator; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index c1582c27ac872b..990aec7eb3d071 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -69,6 +69,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dml/display_mode_vba.h" #include "dcn32/dcn32_dccg.h" #include "dcn10/dcn10_resource.h" @@ -639,6 +640,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn321 = { .num_timing_generator = 4, .num_opp = 4, @@ -827,6 +841,22 @@ static struct clock_source *dcn321_clock_source_create( return NULL; } +static struct dio *dcn321_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn321_hubbub_create(struct dc_context *ctx) { int i; @@ -1474,6 +1504,11 @@ static void dcn321_resource_destruct(struct dcn321_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } + if (pool->base.oem_device != NULL) { struct dc *dc = pool->base.oem_device->ctx->dc; @@ -1872,6 +1907,14 @@ static bool dcn321_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn321_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs, TGs, ABMs */ for (i = 0, j = 0; i < pool->base.res_cap->num_timing_generator; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 3494a40cea99f7..598b2f25881da2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -71,6 +71,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -664,6 +665,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn35 = { .num_timing_generator = 4, .num_opp = 4, @@ -973,6 +987,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn35_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1563,6 +1593,11 @@ static void dcn35_resource_destruct(struct dcn35_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2033,6 +2068,14 @@ static bool dcn35_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn35_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 080bc7f24ffaac..7e15d07df7a33d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -50,6 +50,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -644,6 +645,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn351 = { .num_timing_generator = 4, .num_opp = 4, @@ -953,6 +967,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn351_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1543,6 +1573,11 @@ static void dcn351_resource_destruct(struct dcn351_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2005,6 +2040,14 @@ static bool dcn351_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn351_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index af51ac4ea59e20..83fee2ca61bffa 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -50,6 +50,7 @@ #include "dce/dce_hwseq.h" #include "clk_mgr.h" #include "dio/virtual/virtual_stream_encoder.h" +#include "dio/dcn10/dcn10_dio.h" #include "dce110/dce110_resource.h" #include "dml/display_mode_vba.h" #include "dcn35/dcn35_dccg.h" @@ -651,6 +652,19 @@ static const struct dcn20_vmid_mask vmid_masks = { DCN20_VMID_MASK_SH_LIST(_MASK) }; +static struct dcn_dio_registers dio_regs; + +#define DIO_MASK_SH_LIST(mask_sh)\ + HWS_SF(, DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh) + +static const struct dcn_dio_shift dio_shift = { + DIO_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn_dio_mask dio_mask = { + DIO_MASK_SH_LIST(_MASK) +}; + static const struct resource_caps res_cap_dcn36 = { .num_timing_generator = 4, .num_opp = 4, @@ -960,6 +974,22 @@ static struct mpc *dcn35_mpc_create( return &mpc30->base; } +static struct dio *dcn36_dio_create(struct dc_context *ctx) +{ + struct dcn10_dio *dio10 = kzalloc_obj(struct dcn10_dio); + + if (!dio10) + return NULL; + +#undef REG_STRUCT +#define REG_STRUCT dio_regs + DIO_REG_LIST_DCN10(); + + dcn10_dio_construct(dio10, ctx, &dio_regs, &dio_shift, &dio_mask); + + return &dio10->base; +} + static struct hubbub *dcn35_hubbub_create(struct dc_context *ctx) { int i; @@ -1550,6 +1580,11 @@ static void dcn36_resource_destruct(struct dcn36_resource_pool *pool) if (pool->base.dccg != NULL) dcn_dccg_destroy(&pool->base.dccg); + + if (pool->base.dio != NULL) { + kfree(TO_DCN10_DIO(pool->base.dio)); + pool->base.dio = NULL; + } } static struct hubp *dcn35_hubp_create( @@ -2012,6 +2047,14 @@ static bool dcn36_resource_construct( goto create_fail; } + /* DIO */ + pool->base.dio = dcn36_dio_create(ctx); + if (pool->base.dio == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create dio!\n"); + goto create_fail; + } + /* HUBPs, DPPs, OPPs and TGs */ for (i = 0; i < pool->base.pipe_count; i++) { pool->base.hubps[i] = dcn35_hubp_create(ctx, i); From 0c4a59df370bea245695c00aaae6ae75747139bd Mon Sep 17 00:00:00 2001 From: Changwoo Min Date: Thu, 2 Apr 2026 11:31:50 +0900 Subject: [PATCH 417/700] sched_ext: Fix is_bpf_migration_disabled() false negative on non-PREEMPT_RCU Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for trampoline.c"), the BPF prolog (__bpf_prog_enter) calls migrate_disable() only when CONFIG_PREEMPT_RCU is enabled, via rcu_read_lock_dont_migrate(). Without CONFIG_PREEMPT_RCU, the prolog never touches migration_disabled, so migration_disabled == 1 always means the task is truly migration-disabled regardless of whether it is the current task. The old unconditional p == current check was a false negative in this case, potentially allowing a migration-disabled task to be dispatched to a remote CPU and triggering scx_error in task_can_run_on_remote_rq(). Only apply the p == current disambiguation when CONFIG_PREEMPT_RCU is enabled, where the ambiguity with the BPF prolog still exists. Fixes: 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for trampoline.c") Cc: stable@vger.kernel.org # v6.18+ Link: https://lore.kernel.org/lkml/20250821090609.42508-8-dongml2@chinatelecom.cn/ Signed-off-by: Changwoo Min Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext_idle.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext_idle.c index 0ae93cd6400467..44c3a50c542c2a 100644 --- a/kernel/sched/ext_idle.c +++ b/kernel/sched/ext_idle.c @@ -860,25 +860,32 @@ static bool check_builtin_idle_enabled(struct scx_sched *sch) * code. * * We can't simply check whether @p->migration_disabled is set in a - * sched_ext callback, because migration is always disabled for the current - * task while running BPF code. + * sched_ext callback, because the BPF prolog (__bpf_prog_enter) may disable + * migration for the current task while running BPF code. * - * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) respectively - * disable and re-enable migration. For this reason, the current task - * inside a sched_ext callback is always a migration-disabled task. + * Since the BPF prolog calls migrate_disable() only when CONFIG_PREEMPT_RCU + * is enabled (via rcu_read_lock_dont_migrate()), migration_disabled == 1 for + * the current task is ambiguous only in that case: it could be from the BPF + * prolog rather than a real migrate_disable() call. * - * Therefore, when @p->migration_disabled == 1, check whether @p is the - * current task or not: if it is, then migration was not disabled before - * entering the callback, otherwise migration was disabled. + * Without CONFIG_PREEMPT_RCU, the BPF prolog never calls migrate_disable(), + * so migration_disabled == 1 always means the task is truly + * migration-disabled. + * + * Therefore, when migration_disabled == 1 and CONFIG_PREEMPT_RCU is enabled, + * check whether @p is the current task or not: if it is, then migration was + * not disabled before entering the callback, otherwise migration was disabled. * * Returns true if @p is migration-disabled, false otherwise. */ static bool is_bpf_migration_disabled(const struct task_struct *p) { - if (p->migration_disabled == 1) - return p != current; - else - return p->migration_disabled; + if (p->migration_disabled == 1) { + if (IS_ENABLED(CONFIG_PREEMPT_RCU)) + return p != current; + return true; + } + return p->migration_disabled; } static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p, From 07712db80857d5d09ae08f3df85a708ecfc3b61f Mon Sep 17 00:00:00 2001 From: Nicholas Carlini Date: Tue, 31 Mar 2026 15:25:32 +0200 Subject: [PATCH 418/700] eventpoll: defer struct eventpoll free to RCU grace period In certain situations, ep_free() in eventpoll.c will kfree the epi->ep eventpoll struct while it still being used by another concurrent thread. Defer the kfree() to an RCU callback to prevent UAF. Fixes: f2e467a48287 ("eventpoll: Fix semi-unbounded recursion") Signed-off-by: Nicholas Carlini Signed-off-by: Christian Brauner --- fs/eventpoll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 5714e900567c49..4b43bf41296d4b 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -226,6 +226,9 @@ struct eventpoll { */ refcount_t refcount; + /* used to defer freeing past ep_get_upwards_depth_proc() RCU walk */ + struct rcu_head rcu; + #ifdef CONFIG_NET_RX_BUSY_POLL /* used to track busy poll napi_id */ unsigned int napi_id; @@ -819,7 +822,8 @@ static void ep_free(struct eventpoll *ep) mutex_destroy(&ep->mtx); free_uid(ep->user); wakeup_source_unregister(ep->ws); - kfree(ep); + /* ep_get_upwards_depth_proc() may still hold epi->ep under RCU */ + kfree_rcu(ep, rcu); } /* From 857fa8f2a5b184c206c703a3d9ce05cea683cfed Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 1 Apr 2026 12:23:23 +0200 Subject: [PATCH 419/700] accel: ethosu: Add hardware dependency hint The Ethos-U NPU is only available on ARM systems, so add a hardware dependency hint to prevent this driver from being needlessly included in kernels built for other architectures. Signed-off-by: Jean Delvare Link: https://patch.msgid.link/20260401122323.6127a77c@endymion Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/ethosu/Kconfig b/drivers/accel/ethosu/Kconfig index d25f9b3eb31764..f68e6e286903aa 100644 --- a/drivers/accel/ethosu/Kconfig +++ b/drivers/accel/ethosu/Kconfig @@ -4,6 +4,7 @@ config DRM_ACCEL_ARM_ETHOSU tristate "Arm Ethos-U65/U85 NPU" depends on HAS_IOMEM depends on DRM_ACCEL + depends on ARM || ARM64 || COMPILE_TEST select DRM_GEM_DMA_HELPER select DRM_SCHED select GENERIC_ALLOCATOR From 8eceab19eba9dcbfd2a0daec72e1bf48aa100170 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Thu, 2 Apr 2026 23:34:55 +0800 Subject: [PATCH 420/700] crypto: af_alg - limit RX SG extraction by receive buffer budget Make af_alg_get_rsgl() limit each RX scatterlist extraction to the remaining receive buffer budget. af_alg_get_rsgl() currently uses af_alg_readable() only as a gate before extracting data into the RX scatterlist. Limit each extraction to the remaining af_alg_rcvbuf(sk) budget so that receive-side accounting matches the amount of data attached to the request. If skcipher cannot obtain enough RX space for at least one chunk while more data remains to be processed, reject the recvmsg call instead of rounding the request length down to zero. Fixes: e870456d8e7c8d57c059ea479b5aadbb55ff4c3a ("crypto: algif_skcipher - overhaul memory management") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Douya Le Signed-off-by: Ren Wei Signed-off-by: Herbert Xu --- crypto/af_alg.c | 2 ++ crypto/algif_skcipher.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 8e0199394984d8..437f3e77c7e0e4 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -1229,6 +1229,8 @@ int af_alg_get_rsgl(struct sock *sk, struct msghdr *msg, int flags, seglen = min_t(size_t, (maxsize - len), msg_data_left(msg)); + /* Never pin more pages than the remaining RX accounting budget. */ + seglen = min_t(size_t, seglen, af_alg_rcvbuf(sk)); if (list_empty(&areq->rsgl_list)) { rsgl = &areq->first_rsgl; diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 82735e51be108d..ba0a17fd95aca2 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -130,6 +130,11 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, * full block size buffers. */ if (ctx->more || len < ctx->used) { + if (len < bs) { + err = -EINVAL; + goto free; + } + len -= len % bs; cflags |= CRYPTO_SKCIPHER_REQ_NOTFINAL; } From 2fd68c7ea2ae741a4446d54c8a461255022e2dc7 Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Wed, 1 Apr 2026 10:28:39 +0800 Subject: [PATCH 421/700] MAINTAINERS: orphan PPP over Ethernet driver We haven't seen activities from Michal Ostrowski for quite a long time. The last commit from him is fb64bb560e18 ("PPPoE: Fix flush/close races."), which was in 2009. Email to mostrows@earthlink.net also bounces. Signed-off-by: Qingfang Deng Link: https://patch.msgid.link/20260401022842.15082-1-dqfext@gmail.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c3fe46d7c4bc43..c6e226ae109d68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21075,8 +21075,7 @@ F: include/uapi/linux/atmppp.h F: net/atm/pppoatm.c PPP OVER ETHERNET -M: Michal Ostrowski -S: Maintained +S: Orphan F: drivers/net/ppp/pppoe.c F: drivers/net/ppp/pppox.c From e9c9f084cd78a58e2331fbf83c3d5625fb86e33a Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Wed, 1 Apr 2026 17:58:33 -0700 Subject: [PATCH 422/700] MAINTAINERS: Update email for Allison Henderson Switch active email address to kernel.org alias Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260402005833.38376-1-achender@kernel.org Signed-off-by: Jakub Kicinski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c6e226ae109d68..4601903a8f9b34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22129,7 +22129,7 @@ S: Supported F: drivers/infiniband/sw/rdmavt RDS - RELIABLE DATAGRAM SOCKETS -M: Allison Henderson +M: Allison Henderson L: netdev@vger.kernel.org L: linux-rdma@vger.kernel.org L: rds-devel@oss.oracle.com (moderated for non-subscribers) From 6dede3967619b5944003227a5d09fdc21ed57d10 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 1 Apr 2026 22:12:18 +0100 Subject: [PATCH 423/700] net: altera-tse: fix skb leak on DMA mapping error in tse_start_xmit() When dma_map_single() fails in tse_start_xmit(), the function returns NETDEV_TX_OK without freeing the skb. Since NETDEV_TX_OK tells the stack the packet was consumed, the skb is never freed, leaking memory on every DMA mapping failure. Add dev_kfree_skb_any() before returning to properly free the skb. Fixes: bbd2190ce96d ("Altera TSE: Add main and header file for Altera Ethernet Driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Link: https://patch.msgid.link/20260401211218.279185-1-devnexen@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/altera/altera_tse_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 4342e2d026f82b..9eed0be4411e44 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -570,6 +570,7 @@ static netdev_tx_t tse_start_xmit(struct sk_buff *skb, struct net_device *dev) DMA_TO_DEVICE); if (dma_mapping_error(priv->device, dma_addr)) { netdev_err(priv->dev, "%s: DMA mapping error\n", __func__); + dev_kfree_skb_any(skb); ret = NETDEV_TX_OK; goto out; } From 51f4e090b9f87b40c21b6daadb5c06e6c0a07b67 Mon Sep 17 00:00:00 2001 From: Tyllis Xu Date: Tue, 31 Mar 2026 23:47:07 -0500 Subject: [PATCH 424/700] net: stmmac: fix integer underflow in chain mode The jumbo_frm() chain-mode implementation unconditionally computes len = nopaged_len - bmax; where nopaged_len = skb_headlen(skb) (linear bytes only) and bmax is BUF_SIZE_8KiB or BUF_SIZE_2KiB. However, the caller stmmac_xmit() decides to invoke jumbo_frm() based on skb->len (total length including page fragments): is_jumbo = stmmac_is_jumbo_frm(priv, skb->len, enh_desc); When a packet has a small linear portion (nopaged_len <= bmax) but a large total length due to page fragments (skb->len > bmax), the subtraction wraps as an unsigned integer, producing a huge len value (~0xFFFFxxxx). This causes the while (len != 0) loop to execute hundreds of thousands of iterations, passing skb->data + bmax * i pointers far beyond the skb buffer to dma_map_single(). On IOMMU-less SoCs (the typical deployment for stmmac), this maps arbitrary kernel memory to the DMA engine, constituting a kernel memory disclosure and potential memory corruption from hardware. Fix this by introducing a buf_len local variable clamped to min(nopaged_len, bmax). Computing len = nopaged_len - buf_len is then always safe: it is zero when the linear portion fits within a single descriptor, causing the while (len != 0) loop to be skipped naturally, and the fragment loop in stmmac_xmit() handles page fragments afterward. Fixes: 286a83721720 ("stmmac: add CHAINED descriptor mode support (V4)") Cc: stable@vger.kernel.org Signed-off-by: Tyllis Xu Link: https://patch.msgid.link/20260401044708.1386919-1-LivelyCarpet87@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 120a009c999299..37f9417c7c0e04 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -20,7 +20,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb, unsigned int nopaged_len = skb_headlen(skb); struct stmmac_priv *priv = tx_q->priv_data; unsigned int entry = tx_q->cur_tx; - unsigned int bmax, des2; + unsigned int bmax, buf_len, des2; unsigned int i = 1, len; struct dma_desc *desc; @@ -31,17 +31,18 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb, else bmax = BUF_SIZE_2KiB; - len = nopaged_len - bmax; + buf_len = min_t(unsigned int, nopaged_len, bmax); + len = nopaged_len - buf_len; des2 = dma_map_single(priv->device, skb->data, - bmax, DMA_TO_DEVICE); + buf_len, DMA_TO_DEVICE); desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].len = buf_len; /* do not close the descriptor and do not set own bit */ - stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum, STMMAC_CHAIN_MODE, + stmmac_prepare_tx_desc(priv, desc, 1, buf_len, csum, STMMAC_CHAIN_MODE, 0, false, skb->len); while (len != 0) { From e5d5aef802a5f41283084f7d443ef4fd4b65d86d Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Fri, 3 Apr 2026 09:47:36 +0800 Subject: [PATCH 425/700] ALSA: aoa/onyx: Fix OF node leak on probe failure Add missing of_node_put() in the error path. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260403014736.33014-1-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/aoa/codecs/onyx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 04961c456d2c50..da0eebf5dfbc2d 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -980,10 +980,12 @@ static int onyx_i2c_probe(struct i2c_client *client) onyx->codec.node = of_node_get(node); if (aoa_codec_register(&onyx->codec)) { - goto fail; + goto fail_put; } printk(KERN_DEBUG PFX "created and attached onyx instance\n"); return 0; + fail_put: + of_node_put(onyx->codec.node); fail: kfree(onyx); return -ENODEV; From 6692ed9b4ced29aa819c95cc4ad9e2dc8720c081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 3 Apr 2026 00:21:34 -0300 Subject: [PATCH 426/700] ALSA: hda: Notify IEC958 Default PCM switch state changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "IEC958 Default PCM Playback Switch" control is backed directly by mout->share_spdif. The share-switch callbacks currently access that state without serialization, and spdif_share_sw_put() always returns 0, so normal userspace writes never emit the standard ALSA control value notification. snd_hda_multi_out_analog_open() may also clear mout->share_spdif when the analog PCM capabilities and the SPDIF capabilities no longer intersect. That fallback is still needed to avoid creating an impossible hw constraint set, but it changes the mixer backing value without notifying subscribers. Protect the share-switch callbacks with spdif_mutex like the other SPDIF control handlers, return the actual change value from spdif_share_sw_put(), and notify the cached control when the open path forcibly disables shared SPDIF mode after dropping spdif_mutex. This keeps the existing auto-disable behavior while making switch state changes visible to userspace. Fixes: 9a08160bdbe3 ("[ALSA] hda-codec - Add "IEC958 Default PCM" switch") Fixes: 022b466fc353 ("ALSA: hda - Avoid invalid formats and rates with shared SPDIF") Suggested-by: Takashi Iwai Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260403-hda-spdif-share-notify-v3-1-4eb1356b0f17@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/common/codec.c | 46 +++++++++++++++++++++++++++++------- sound/hda/common/hda_local.h | 1 + 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 09b1329bb8f351..5123df32ad89f5 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -2529,7 +2529,10 @@ EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign); static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_multi_out *mout = (void *)kcontrol->private_value; + + guard(mutex)(&codec->spdif_mutex); ucontrol->value.integer.value[0] = mout->share_spdif; return 0; } @@ -2537,9 +2540,15 @@ static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - mout->share_spdif = !!ucontrol->value.integer.value[0]; - return 0; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_multi_out *mout = (void *)kcontrol->private_value; + bool val = !!ucontrol->value.integer.value[0]; + int change; + + guard(mutex)(&codec->spdif_mutex); + change = mout->share_spdif != val; + mout->share_spdif = val; + return change; } static const struct snd_kcontrol_new spdif_share_sw = { @@ -2550,6 +2559,14 @@ static const struct snd_kcontrol_new spdif_share_sw = { .put = spdif_share_sw_put, }; +static void notify_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout) +{ + if (mout->share_spdif_kctl) + snd_ctl_notify_one(codec->card, SNDRV_CTL_EVENT_MASK_VALUE, + mout->share_spdif_kctl, 0); +} + /** * snd_hda_create_spdif_share_sw - create Default PCM switch * @codec: the HDA codec @@ -2559,15 +2576,24 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { struct snd_kcontrol *kctl; + int err; if (!mout->dig_out_nid) return 0; - kctl = snd_ctl_new1(&spdif_share_sw, mout); + kctl = snd_ctl_new1(&spdif_share_sw, codec); if (!kctl) return -ENOMEM; - /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); + /* snd_ctl_new1() stores @codec in private_data; stash @mout in + * private_value for the share-switch callbacks and cache the + * assigned control for forced-disable notifications. + */ + kctl->private_value = (unsigned long)mout; + err = snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); + if (err < 0) + return err; + mout->share_spdif_kctl = kctl; + return 0; } EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw); @@ -3701,6 +3727,8 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_pcm_stream *hinfo) { struct snd_pcm_runtime *runtime = substream->runtime; + bool notify_share_sw = false; + runtime->hw.channels_max = mout->max_channels; if (mout->dig_out_nid) { if (!mout->analog_rates) { @@ -3729,10 +3757,12 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, hinfo->maxbps = mout->spdif_maxbps; } else { mout->share_spdif = 0; - /* FIXME: need notify? */ + notify_share_sw = true; } } } + if (notify_share_sw) + notify_spdif_share_sw(codec, mout); return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); } diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index ab423f1cef5492..98b2c4acebc273 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -221,6 +221,7 @@ struct hda_multi_out { unsigned int spdif_rates; unsigned int spdif_maxbps; u64 spdif_formats; + struct snd_kcontrol *share_spdif_kctl; /* cached shared SPDIF switch */ }; int snd_hda_create_spdif_share_sw(struct hda_codec *codec, From f2b1cbef153636fa498324b47e822e8b4d1774aa Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:16 +0200 Subject: [PATCH 427/700] Documentation: minor updates to the security contacts This clarifies the fact that the bug reporters must use a valid e-mail address to send their report, and that the security team assists developers working on a fix but doesn't always produce fixes on its own. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-2-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index c0cf93e11565e2..da7937fd59df15 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -8,6 +8,10 @@ like to know when a security bug is found so that it can be fixed and disclosed as quickly as possible. Please report security bugs to the Linux kernel security team. +Reports are to be sent over e-mail exclusively. Please use a working e-mail +address, preferably the same that you want to appear in ``Reported-by`` tags +if any. If unsure, send your report to yourself first. + The security team and maintainers almost always require additional information beyond what was initially provided in a report and rely on active and efficient collaboration with the reporter to perform further @@ -27,11 +31,9 @@ made public. The Linux kernel security team can be contacted by email at . This is a private list of security officers -who will help verify the bug report and develop and release a fix. -If you already have a fix, please include it with your report, as -that can speed up the process considerably. It is possible that the -security team will bring in extra help from area maintainers to -understand and fix the security vulnerability. +who will help verify the bug report and assist developers working on a fix. +It is possible that the security team will bring in extra help from area +maintainers to understand and fix the security vulnerability. Please send **plain text** emails without attachments where possible. It is much harder to have a context-quoted discussion about a complex From a72b832a482372001a158c8014d116b053089b5d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:17 +0200 Subject: [PATCH 428/700] Documentation: explain how to find maintainers addresses for security reports These days, 80% of the work done by the security team consists in locating the affected subsystem in a report, running get_maintainers on it, forwarding the report to these persons and responding to the reporter with them in Cc. This is a huge and unneeded overhead that we must try to lower for a better overall efficiency. This patch adds a complete section explaining how to figure the list of recipients to send the report to. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-3-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 76 ++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index da7937fd59df15..ac97fc78fecd5b 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -5,8 +5,75 @@ Security bugs Linux kernel developers take security very seriously. As such, we'd like to know when a security bug is found so that it can be fixed and -disclosed as quickly as possible. Please report security bugs to the -Linux kernel security team. +disclosed as quickly as possible. + +Identifying contacts +-------------------- + +The most effective way to report a security bug is to send it directly to the +affected subsystem's maintainers and Cc: the Linux kernel security team. Do +not send it to a public list at this stage, unless you have good reasons to +consider the issue as being public or trivial to discover (e.g. result of a +widely available automated vulnerability scanning tool that can be repeated by +anyone). + +If you're sending a report for issues affecting multiple parts in the kernel, +even if they're fairly similar issues, please send individual messages (think +that maintainers will not all work on the issues at the same time). The only +exception is when an issue concerns closely related parts maintained by the +exact same subset of maintainers, and these parts are expected to be fixed all +at once by the same commit, then it may be acceptable to report them at once. + +One difficulty for most first-time reporters is to figure the right list of +recipients to send a report to. In the Linux kernel, all official maintainers +are trusted, so the consequences of accidentally including the wrong maintainer +are essentially a bit more noise for that person, i.e. nothing dramatic. As +such, a suitable method to figure the list of maintainers (which kernel +security officers use) is to rely on the get_maintainers.pl script, tuned to +only report maintainers. This script, when passed a file name, will look for +its path in the MAINTAINERS file to figure a hierarchical list of relevant +maintainers. Calling it a first time with the finest level of filtering will +most of the time return a short list of this specific file's maintainers:: + + $ ./scripts/get_maintainer.pl --no-l --no-r --pattern-depth 1 \ + drivers/example.c + Developer One (maintainer:example driver) + Developer Two (maintainer:example driver) + +These two maintainers should then receive the message. If the command does not +return anything, it means the affected file is part of a wider subsystem, so we +should be less specific:: + + $ ./scripts/get_maintainer.pl --no-l --no-r drivers/example.c + Developer One (maintainer:example subsystem) + Developer Two (maintainer:example subsystem) + Developer Three (maintainer:example subsystem [GENERAL]) + Developer Four (maintainer:example subsystem [GENERAL]) + +Here, picking the first, most specific ones, is sufficient. When the list is +long, it is possible to produce a comma-delimited e-mail address list on a +single line suitable for use in the To: field of a mailer like this:: + + $ ./scripts/get_maintainer.pl --no-tree --no-l --no-r --no-n --m \ + --no-git-fallback --no-substatus --no-rolestats --no-multiline \ + --pattern-depth 1 drivers/example.c + dev1@example.com, dev2@example.org + +or this for the wider list:: + + $ ./scripts/get_maintainer.pl --no-tree --no-l --no-r --no-n --m \ + --no-git-fallback --no-substatus --no-rolestats --no-multiline \ + drivers/example.c + dev1@example.com, dev2@example.org, dev3@example.com, dev4@example.org + +If at this point you're still facing difficulties spotting the right +maintainers, **and only in this case**, it's possible to send your report to +the Linux kernel security team only. Your message will be triaged, and you +will receive instructions about whom to contact, if needed. Your message may +equally be forwarded as-is to the relevant maintainers. + +Sending the report +------------------ Reports are to be sent over e-mail exclusively. Please use a working e-mail address, preferably the same that you want to appear in ``Reported-by`` tags @@ -29,6 +96,7 @@ information is helpful. Any exploit code is very helpful and will not be released without consent from the reporter unless it has already been made public. +The report must be sent to maintainers, with the security team in ``Cc:``. The Linux kernel security team can be contacted by email at . This is a private list of security officers who will help verify the bug report and assist developers working on a fix. @@ -44,7 +112,9 @@ reproduction steps, and follow it with a proposed fix, all in plain text. Markdown, HTML and RST formatted reports are particularly frowned upon since they're quite hard to read for humans and encourage to use dedicated viewers, sometimes online, which by definition is not acceptable for a confidential -security report. +security report. Note that some mailers tend to mangle formatting of plain +text by default, please consult Documentation/process/email-clients.rst for +more info. Disclosure and embargoed information ------------------------------------ From 496fa1befba1e8ff149af5120cd9c9616bb05120 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 3 Apr 2026 08:20:18 +0200 Subject: [PATCH 429/700] Documentation: clarify the mandatory and desirable info for security reports A significant part of the effort of the security team consists in begging reporters for patch proposals, or asking them to provide them in regular format, and most of the time they're willing to provide this, they just didn't know that it would help. So let's add a section detailing the required and desirable contents in a security report to help reporters write more actionable reports which do not require round trips. Cc: Eric Dumazet Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260403062018.31080-4-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 66 ++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index ac97fc78fecd5b..0b1f6d8e3cbe71 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -7,6 +7,65 @@ Linux kernel developers take security very seriously. As such, we'd like to know when a security bug is found so that it can be fixed and disclosed as quickly as possible. +Preparing your report +--------------------- + +Like with any bug report, a security bug report requires a lot of analysis work +from the developers, so the more information you can share about the issue, the +better. Please review the procedure outlined in +Documentation/admin-guide/reporting-issues.rst if you are unclear about what +information is helpful. The following information are absolutely necessary in +**any** security bug report: + + * **affected kernel version range**: with no version indication, your report + will not be processed. A significant part of reports are for bugs that + have already been fixed, so it is extremely important that vulnerabilities + are verified on recent versions (development tree or latest stable + version), at least by verifying that the code has not changed since the + version where it was detected. + + * **description of the problem**: a detailed description of the problem, with + traces showing its manifestation, and why you consider that the observed + behavior as a problem in the kernel, is necessary. + + * **reproducer**: developers will need to be able to reproduce the problem to + consider a fix as effective. This includes both a way to trigger the issue + and a way to confirm it happens. A reproducer with low complexity + dependencies will be needed (source code, shell script, sequence of + instructions, file-system image etc). Binary-only executables are not + accepted. Working exploits are extremely helpful and will not be released + without consent from the reporter, unless they are already public. By + definition if an issue cannot be reproduced, it is not exploitable, thus it + is not a security bug. + + * **conditions**: if the bug depends on certain configuration options, + sysctls, permissions, timing, code modifications etc, these should be + indicated. + +In addition, the following information are highly desirable: + + * **suspected location of the bug**: the file names and functions where the + bug is suspected to be present are very important, at least to help forward + the report to the appropriate maintainers. When not possible (for example, + "system freezes each time I run this command"), the security team will help + identify the source of the bug. + + * **a proposed fix**: bug reporters who have analyzed the cause of a bug in + the source code almost always have an accurate idea on how to fix it, + because they spent a long time studying it and its implications. Proposing + a tested fix will save maintainers a lot of time, even if the fix ends up + not being the right one, because it helps understand the bug. When + proposing a tested fix, please always format it in a way that can be + immediately merged (see Documentation/process/submitting-patches.rst). + This will save some back-and-forth exchanges if it is accepted, and you + will be credited for finding and fixing this issue. Note that in this case + only a ``Signed-off-by:`` tag is needed, without ``Reported-by:` when the + reporter and author are the same. + + * **mitigations**: very often during a bug analysis, some ways of mitigating + the issue appear. It is useful to share them, as they can be helpful to + keep end users protected during the time it takes them to apply the fix. + Identifying contacts -------------------- @@ -89,13 +148,6 @@ run additional tests. Reports where the reporter does not respond promptly or cannot effectively discuss their findings may be abandoned if the communication does not quickly improve. -As it is with any bug, the more information provided the easier it -will be to diagnose and fix. Please review the procedure outlined in -'Documentation/admin-guide/reporting-issues.rst' if you are unclear about what -information is helpful. Any exploit code is very helpful and will not -be released without consent from the reporter unless it has already been -made public. - The report must be sent to maintainers, with the security team in ``Cc:``. The Linux kernel security team can be contacted by email at . This is a private list of security officers From d733fb463834cf97a0c667681e236fea0e833a05 Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Fri, 3 Apr 2026 09:23:35 +0100 Subject: [PATCH 430/700] ASoC: intel: sof_sdw: Prepare for configuration without a jack In certain setups of cs42l43 UAJ function may be removed from ACPI and physically unconnected. Prepare a driver for that configuration by setting a system clock in the speaker path too. Signed-off-by: Maciej Strozek Link: https://patch.msgid.link/20260403082335.40798-1-mstrozek@opensource.cirrus.com Signed-off-by: Mark Brown --- 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 2685ff4f093206..4a451b9d4f1374 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; } From 0178e64123129f56fc153b9d53121318fd71bdfc Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Fri, 3 Apr 2026 12:04:25 +0530 Subject: [PATCH 431/700] ASoC: amd: acp-sdw-legacy: remove unnecessary condition check Currently there is no mechanism to read dmic_num in mach_params structure. In this scenario mach_params->dmic_num check always returns 0. Remove unnecessary condition check for mach_params->dmic_num. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20260403063452.159800-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 05484c72f9a881..24e99d8d36ab2e 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -378,10 +378,8 @@ static int create_dmic_dailinks(struct snd_soc_card *card, static int soc_card_dai_links_create(struct snd_soc_card *card) { struct device *dev = card->dev; - struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); int sdw_be_num = 0, dmic_num = 0; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; struct snd_soc_aux_dev *soc_aux; struct snd_soc_codec_conf *codec_conf; struct snd_soc_dai_link *dai_links; @@ -424,7 +422,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) sdw_be_num = ret; /* enable dmic */ - if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num) + if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) dmic_num = 1; dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); From f9e437cddf6cf9e603bdaefe148c1f4792aaf39c Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Fri, 27 Mar 2026 13:33:11 +0300 Subject: [PATCH 432/700] ASoC: rsnd: Fix potential out-of-bounds access of component_dais[] component_dais[RSND_MAX_COMPONENT] is initially zero-initialized and later populated in rsnd_dai_of_node(). However, the existing boundary check: if (i >= RSND_MAX_COMPONENT) does not guarantee that the last valid element remains zero. As a result, the loop can rely on component_dais[RSND_MAX_COMPONENT] being zero, which may lead to an out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 547b02f74e4a ("ASoC: rsnd: enable multi Component support for Audio Graph Card/Card2") Signed-off-by: Denis Rastyogin Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260327103311.459239-1-gerben@altlinux.org Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index 69fb19964a71d2..2dc078358612d7 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -1974,7 +1974,7 @@ static int rsnd_probe(struct platform_device *pdev) * asoc register */ ci = 0; - for (i = 0; priv->component_dais[i] > 0; i++) { + for (i = 0; i < RSND_MAX_COMPONENT && priv->component_dais[i] > 0; i++) { int nr = priv->component_dais[i]; ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, From 9557ec3f9c9fc9ca737f0bc963088193132fd967 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 27 Mar 2026 16:27:32 +0000 Subject: [PATCH 433/700] ASoC: SDCA: Add RJ support to class driver Add the retaskable jack Function to the list of Functions supported by the class driver, it shouldn't require anything that isn't already supported. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260327162732.877257-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 98fd3fd1052b42..c3eb3c23a7db97 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -368,8 +368,14 @@ static int class_function_probe(struct auxiliary_device *auxdev, return dev_err_probe(dev, PTR_ERR(drv->regmap), "failed to create regmap"); - if (desc->type == SDCA_FUNCTION_TYPE_UAJ) + switch (desc->type) { + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: cmp_drv->set_jack = class_function_set_jack; + break; + default: + break; + } ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv, &dais, &num_dais, @@ -539,6 +545,10 @@ static const struct auxiliary_device_id class_function_id_table[] = { .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_HID_NAME, .driver_data = SDCA_FUNCTION_TYPE_HID, }, + { + .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_RJ_NAME, + .driver_data = SDCA_FUNCTION_TYPE_RJ, + }, {}, }; MODULE_DEVICE_TABLE(auxiliary, class_function_id_table); From 04cc47624a33a6b56c62bfd65ad6b2d1c42e17c4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 Apr 2026 19:50:40 -0700 Subject: [PATCH 434/700] ASoC: codecs: tlv320dac33: remove kmemdup_array Use a flexible array member and struct_size to use one allocation. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260402025040.93569-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 605da1259fc600..223c49dfc4506f 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -91,7 +91,6 @@ struct tlv320dac33_priv { int mode1_latency; /* latency caused by the i2c writes in * us */ u8 burst_bclkdiv; /* BCLK divider value in burst mode */ - u8 *reg_cache; unsigned int burst_rate; /* Interface speed in Burst modes */ int keep_bclk; /* Keep the BCLK continuously running @@ -108,6 +107,8 @@ struct tlv320dac33_priv { enum dac33_state state; struct i2c_client *i2c; + + u8 reg_cache[]; }; static const u8 dac33_reg[DAC33_CACHEREGNUM] = { @@ -1477,15 +1478,12 @@ static int dac33_i2c_probe(struct i2c_client *client) struct tlv320dac33_priv *dac33; int ret, i; - dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv), + dac33 = devm_kzalloc(&client->dev, struct_size(dac33, reg_cache, ARRAY_SIZE(dac33_reg)), GFP_KERNEL); if (dac33 == NULL) return -ENOMEM; - dac33->reg_cache = devm_kmemdup_array(&client->dev, dac33_reg, ARRAY_SIZE(dac33_reg), - sizeof(dac33_reg[0]), GFP_KERNEL); - if (!dac33->reg_cache) - return -ENOMEM; + memcpy(dac33->reg_cache, dac33_reg, ARRAY_SIZE(dac33_reg)); dac33->i2c = client; mutex_init(&dac33->mutex); From ba2a0e81d4d71c3bbc61c420b6fac5abaeddd77d Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Wed, 1 Apr 2026 18:51:45 +0530 Subject: [PATCH 435/700] ASoC: SDCA: Export Q7.8 volume control helpers Export the Q7.8 volume control helpers to allow reuse by other ASoC drivers. These functions handle 16-bit signed Q7.8 fixed-point format values for volume controls. Changes include: - Rename q78_get_volsw to sdca_asoc_q78_get_volsw - Rename q78_put_volsw to sdca_asoc_q78_put_volsw - Add a convenience macro SDCA_SINGLE_Q78_TLV and SDCA_DOUBLE_Q78_TLV for creating mixer controls This allows other ASoC drivers to easily implement controls using the Q7.8 fixed-point format without duplicating code. Signed-off-by: Niranjan H Y Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260401132148.2367-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 43 +++++++++++++++++++++++++++++++++++++- sound/soc/sdca/sdca_asoc.c | 14 +++++++------ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index aa9124f932189b..46a61a52decc5b 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -13,6 +13,8 @@ struct device; struct regmap; struct sdca_function_data; +struct snd_ctl_elem_value; +struct snd_kcontrol; struct snd_kcontrol_new; struct snd_pcm_hw_params; struct snd_pcm_substream; @@ -23,6 +25,42 @@ struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +/* convenient macro to handle the mono volume in 7.8 fixed format representation */ +#define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg), .rreg = (xreg), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + +/* convenient macro for stereo volume in 7.8 fixed format with separate registers for L/R */ +#define SDCA_DOUBLE_Q78_TLV(xname, xreg_l, xreg_r, xmin, xmax, xstep, tlv_array) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = sdca_asoc_q78_get_volsw, \ + .put = sdca_asoc_q78_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = (xreg_l), .rreg = (xreg_r), \ + .min = (xmin), .max = (xmax), \ + .shift = (xstep), .rshift = (xstep), \ + .sign_bit = 15 \ + } \ +} + int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function, int *num_widgets, int *num_routes, int *num_controls, int *num_dais); @@ -57,5 +95,8 @@ int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); - +int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); #endif // __SDCA_ASOC_H__ diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 7709a4ce26e097..2bfc8e5aee31d9 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -820,8 +820,8 @@ static int q78_write(struct snd_soc_component *component, return snd_soc_component_update_bits(component, reg, mask, reg_val); } -static int q78_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); @@ -841,6 +841,7 @@ static int q78_put_volsw(struct snd_kcontrol *kcontrol, return ret; } +EXPORT_SYMBOL_NS(sdca_asoc_q78_put_volsw, "SND_SOC_SDCA"); static int q78_read(struct snd_soc_component *component, struct soc_mixer_control *mc, unsigned int reg) @@ -855,8 +856,8 @@ static int q78_read(struct snd_soc_component *component, return val & GENMASK(mc->sign_bit, 0); } -static int q78_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); @@ -868,6 +869,7 @@ static int q78_get_volsw(struct snd_kcontrol *kcontrol, return 0; } +EXPORT_SYMBOL_NS(sdca_asoc_q78_get_volsw, "SND_SOC_SDCA"); static int control_limit_kctl(struct device *dev, struct sdca_entity *entity, @@ -912,8 +914,8 @@ static int control_limit_kctl(struct device *dev, kctl->tlv.p = tlv; kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - kctl->get = q78_get_volsw; - kctl->put = q78_put_volsw; + kctl->get = sdca_asoc_q78_get_volsw; + kctl->put = sdca_asoc_q78_put_volsw; return 0; } From ccd7e53b7d6f06b0fef0f657c8cf377be2e8978d Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Thu, 26 Mar 2026 14:56:14 +0900 Subject: [PATCH 436/700] ASoC: imx-rpmsg: Add DSD format support with dynamic DAI format switching Add hw_params callback to dynamically switch DAI format between I2S and PDM based on audio stream format. When DSD formats are detected, the DAI format is switched to PDM mode. Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260326055614.3614104-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-rpmsg.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 76a8e68c1b620c..40e0043cfe15ee 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -30,6 +30,53 @@ static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = { SND_SOC_DAPM_MIC("Main MIC", NULL), }; +static int imx_rpmsg_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + snd_pcm_format_t format = params_format(params); + struct device *dev = rtd->card->dev; + unsigned int fmt = rtd->dai_link->dai_fmt; + bool format_is_dsd = false; + int ret; + + switch (format) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + format_is_dsd = true; + break; + default: + format_is_dsd = false; + break; + } + + if (format_is_dsd) + fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | + SND_SOC_DAIFMT_PDM; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set cpu dai fmt: %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set codec dai fmt: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_ops imx_rpmsg_ops = { + .hw_params = imx_rpmsg_hw_params, +}; + static int imx_rpmsg_late_probe(struct snd_soc_card *card) { struct imx_rpmsg *data = snd_soc_card_get_drvdata(card); @@ -135,6 +182,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + data->dai.ops = &imx_rpmsg_ops; /* * i.MX rpmsg sound cards work on codec slave mode. MCLK will be From a02496a29463e7f0d1643e83aab28adb3dd03f1a Mon Sep 17 00:00:00 2001 From: Aravind Anilraj Date: Wed, 1 Apr 2026 18:05:04 -0400 Subject: [PATCH 437/700] ASoC: Intel: bytcr_rt5640: Fix MCLK leak on platform_clock_control error If byt_rt5640_prepare_and_enable_pll1() fails, the function returns without calling clk_disable_unprepare() on priv->mclk, which was already enabled earlier in the same code path. Add the missing cleanup call to prevent the clock from leaking. Signed-off-by: Aravind Anilraj Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260401220507.23557-2-aravindanilraj0702@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 103e0b445603f2..e4c21c9c5b38c7 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -289,6 +289,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000); + if (ret < 0) + clk_disable_unprepare(priv->mclk); } else { /* * Set codec clock source to internal clock before From b022e5c142efe4c5497e6cfda1f143618b4b9254 Mon Sep 17 00:00:00 2001 From: Aravind Anilraj Date: Wed, 1 Apr 2026 18:05:05 -0400 Subject: [PATCH 438/700] ASoC: Intel: bytcr_rt5651: Fix MCLK leak on platform_clock_control error If byt_rt5651_prepare_and_enable_pll1() fails, the function returns without calling clk_disable_unprepare() on priv->mclk, which was already enabled earlier in the same code path. Add the missing cleanup call to prevent the clock from leaking. Signed-off-by: Aravind Anilraj Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260401220507.23557-3-aravindanilraj0702@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5651.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 68cf463f1d5070..8932fc5d6f4f25 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -209,6 +209,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50); + if (ret < 0) + clk_disable_unprepare(priv->mclk); } else { /* * Set codec clock source to internal clock before From dced5a373a96cfd9f3bd0ffcf5339a7579d1473a Mon Sep 17 00:00:00 2001 From: Aravind Anilraj Date: Wed, 1 Apr 2026 18:05:06 -0400 Subject: [PATCH 439/700] ASoC: Intel: cht_bsw_rt5672: Fix MCLK leak on platform_clock_control error If snd_soc_dai_set_pll() or snd_soc_dai_set_sysclk() fail inside the EVENT_ON path, the function returns without calling clk_disable_unprepare() on ctx->mclk, which was already enabled earlier in the same code path. Add the missing clk_disable_unprepare() calls before returning the error. Signed-off-by: Aravind Anilraj Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260401220507.23557-4-aravindanilraj0702@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/cht_bsw_rt5672.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 359723f2700e46..57d6997eb12fff 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -77,6 +77,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, CHT_PLAT_CLK_3_HZ, 48000 * 512); if (ret < 0) { dev_err(card->dev, "can't set codec pll: %d\n", ret); + if (ctx->mclk) + clk_disable_unprepare(ctx->mclk); return ret; } @@ -85,6 +87,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, 48000 * 512, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + if (ctx->mclk) + clk_disable_unprepare(ctx->mclk); return ret; } } else { From 85c38c2fa17da23b2258edd96c89e63f92426ba5 Mon Sep 17 00:00:00 2001 From: Aravind Anilraj Date: Wed, 1 Apr 2026 18:05:07 -0400 Subject: [PATCH 440/700] ASoC: Intel: Standardize MCLK error logs across RT boards Standardize the error logging in platform_clock_control() by adding missing newline characters to dev_err() strings. Additionally, include the return code in the error messages to assist with debugging. Signed-off-by: Aravind Anilraj Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260401220507.23557-5-aravindanilraj0702@gmail.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 2 +- sound/soc/intel/boards/bytcr_rt5651.c | 2 +- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index e4c21c9c5b38c7..40da3eea5fa72e 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -285,7 +285,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_ON(event)) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { - dev_err(card->dev, "could not configure MCLK state\n"); + dev_err(card->dev, "could not configure MCLK state: %d\n", ret); return ret; } ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 8932fc5d6f4f25..62cb4856c7972d 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -205,7 +205,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_ON(event)) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { - dev_err(card->dev, "could not configure MCLK state"); + dev_err(card->dev, "could not configure MCLK state: %d\n", ret); return ret; } ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 57d6997eb12fff..fd4cefd298d216 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -67,7 +67,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ret = clk_prepare_enable(ctx->mclk); if (ret < 0) { dev_err(card->dev, - "could not configure MCLK state"); + "could not configure MCLK state: %d\n", ret); return ret; } } From 7e0ffb72de8aa3b25989c2d980e81b829c577010 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 3 Apr 2026 08:57:20 +0200 Subject: [PATCH 441/700] sched_ext: Fix stale direct dispatch state in ddsp_dsq_id @p->scx.ddsp_dsq_id can be left set (non-SCX_DSQ_INVALID) triggering a spurious warning in mark_direct_dispatch() when the next wakeup's ops.select_cpu() calls scx_bpf_dsq_insert(), such as: WARNING: kernel/sched/ext.c:1273 at scx_dsq_insert_commit+0xcd/0x140 The root cause is that ddsp_dsq_id was only cleared in dispatch_enqueue(), which is not reached in all paths that consume or cancel a direct dispatch verdict. Fix it by clearing it at the right places: - direct_dispatch(): cache the direct dispatch state in local variables and clear it before dispatch_enqueue() on the synchronous path. For the deferred path, the direct dispatch state must remain set until process_ddsp_deferred_locals() consumes them. - process_ddsp_deferred_locals(): cache the dispatch state in local variables and clear it before calling dispatch_to_local_dsq(), which may migrate the task to another rq. - do_enqueue_task(): clear the dispatch state on the enqueue path (local/global/bypass fallbacks), where the direct dispatch verdict is ignored. - dequeue_task_scx(): clear the dispatch state after dispatch_dequeue() to handle both the deferred dispatch cancellation and the holding_cpu race, covering all cases where a pending direct dispatch is cancelled. - scx_disable_task(): clear the direct dispatch state when transitioning a task out of the current scheduler. Waking tasks may have had the direct dispatch state set by the outgoing scheduler's ops.select_cpu() and then been queued on a wake_list via ttwu_queue_wakelist(), when SCX_OPS_ALLOW_QUEUED_WAKEUP is set. Such tasks are not on the runqueue and are not iterated by scx_bypass(), so their direct dispatch state won't be cleared. Without this clear, any subsequent SCX scheduler that tries to direct dispatch the task will trigger the WARN_ON_ONCE() in mark_direct_dispatch(). Fixes: 5b26f7b920f7 ("sched_ext: Allow SCX_DSQ_LOCAL_ON for direct dispatches") Cc: stable@vger.kernel.org # v6.12+ Cc: Daniel Hodges Cc: Patrick Somaru Signed-off-by: Andrea Righi Signed-off-by: Tejun Heo --- kernel/sched/ext.c | 49 +++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index d5bdcdb3f70041..064eaa76be4b9f 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -1109,15 +1109,6 @@ static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq, dsq_mod_nr(dsq, 1); p->scx.dsq = dsq; - /* - * scx.ddsp_dsq_id and scx.ddsp_enq_flags are only relevant on the - * direct dispatch path, but we clear them here because the direct - * dispatch verdict may be overridden on the enqueue path during e.g. - * bypass. - */ - p->scx.ddsp_dsq_id = SCX_DSQ_INVALID; - p->scx.ddsp_enq_flags = 0; - /* * We're transitioning out of QUEUEING or DISPATCHING. store_release to * match waiters' load_acquire. @@ -1283,12 +1274,34 @@ static void mark_direct_dispatch(struct scx_sched *sch, p->scx.ddsp_enq_flags = enq_flags; } +/* + * Clear @p direct dispatch state when leaving the scheduler. + * + * Direct dispatch state must be cleared in the following cases: + * - direct_dispatch(): cleared on the synchronous enqueue path, deferred + * dispatch keeps the state until consumed + * - process_ddsp_deferred_locals(): cleared after consuming deferred state, + * - do_enqueue_task(): cleared on enqueue fallbacks where the dispatch + * verdict is ignored (local/global/bypass) + * - dequeue_task_scx(): cleared after dispatch_dequeue(), covering deferred + * cancellation and holding_cpu races + * - scx_disable_task(): cleared for queued wakeup tasks, which are excluded by + * the scx_bypass() loop, so that stale state is not reused by a subsequent + * scheduler instance + */ +static inline void clear_direct_dispatch(struct task_struct *p) +{ + p->scx.ddsp_dsq_id = SCX_DSQ_INVALID; + p->scx.ddsp_enq_flags = 0; +} + static void direct_dispatch(struct scx_sched *sch, struct task_struct *p, u64 enq_flags) { struct rq *rq = task_rq(p); struct scx_dispatch_q *dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p); + u64 ddsp_enq_flags; touch_core_sched_dispatch(rq, p); @@ -1329,8 +1342,10 @@ static void direct_dispatch(struct scx_sched *sch, struct task_struct *p, return; } - dispatch_enqueue(sch, dsq, p, - p->scx.ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS); + ddsp_enq_flags = p->scx.ddsp_enq_flags; + clear_direct_dispatch(p); + + dispatch_enqueue(sch, dsq, p, ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS); } static bool scx_rq_online(struct rq *rq) @@ -1439,6 +1454,7 @@ static void do_enqueue_task(struct rq *rq, struct task_struct *p, u64 enq_flags, */ touch_core_sched(rq, p); refill_task_slice_dfl(sch, p); + clear_direct_dispatch(p); dispatch_enqueue(sch, dsq, p, enq_flags); } @@ -1610,6 +1626,7 @@ static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int deq_flags sub_nr_running(rq, 1); dispatch_dequeue(rq, p); + clear_direct_dispatch(p); return true; } @@ -2293,13 +2310,15 @@ static void process_ddsp_deferred_locals(struct rq *rq) struct task_struct, scx.dsq_list.node))) { struct scx_sched *sch = scx_root; struct scx_dispatch_q *dsq; + u64 dsq_id = p->scx.ddsp_dsq_id; + u64 enq_flags = p->scx.ddsp_enq_flags; list_del_init(&p->scx.dsq_list.node); + clear_direct_dispatch(p); - dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p); + dsq = find_dsq_for_dispatch(sch, rq, dsq_id, p); if (!WARN_ON_ONCE(dsq->id != SCX_DSQ_LOCAL)) - dispatch_to_local_dsq(sch, rq, dsq, p, - p->scx.ddsp_enq_flags); + dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags); } } @@ -3015,6 +3034,8 @@ static void scx_disable_task(struct task_struct *p) lockdep_assert_rq_held(rq); WARN_ON_ONCE(scx_get_task_state(p) != SCX_TASK_ENABLED); + clear_direct_dispatch(p); + if (SCX_HAS_OP(sch, disable)) SCX_CALL_OP_TASK(sch, SCX_KF_REST, disable, rq, p); scx_set_task_state(p, SCX_TASK_READY); From c842743d073bdd683606cb414eb0ca84465dd834 Mon Sep 17 00:00:00 2001 From: Ruide Cao Date: Thu, 2 Apr 2026 22:46:20 +0800 Subject: [PATCH 442/700] net: sched: act_csum: validate nested VLAN headers tcf_csum_act() walks nested VLAN headers directly from skb->data when an skb still carries in-payload VLAN tags. The current code reads vlan->h_vlan_encapsulated_proto and then pulls VLAN_HLEN bytes without first ensuring that the full VLAN header is present in the linear area. If only part of an inner VLAN header is linearized, accessing h_vlan_encapsulated_proto reads past the linear area, and the following skb_pull(VLAN_HLEN) may violate skb invariants. Fix this by requiring pskb_may_pull(skb, VLAN_HLEN) before accessing and pulling each nested VLAN header. If the header still is not fully available, drop the packet through the existing error path. Fixes: 2ecba2d1e45b ("net: sched: act_csum: Fix csum calc for tagged packets") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Ruide Cao Signed-off-by: Ren Wei Reviewed-by: Simon Horman Link: https://patch.msgid.link/22df2fcb49f410203eafa5d97963dd36089f4ecf.1774892775.git.caoruide123@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/act_csum.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 213e1ce9d2da03..a9e4635d899e38 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -604,8 +604,12 @@ TC_INDIRECT_SCOPE int tcf_csum_act(struct sk_buff *skb, protocol = skb->protocol; orig_vlan_tag_present = true; } else { - struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data; + struct vlan_hdr *vlan; + if (!pskb_may_pull(skb, VLAN_HLEN)) + goto drop; + + vlan = (struct vlan_hdr *)skb->data; protocol = vlan->h_vlan_encapsulated_proto; skb_pull(skb, VLAN_HLEN); skb_reset_network_header(skb); From e16a0d36777b572196de4944aaa196adf828eb8e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Apr 2026 16:10:40 +0200 Subject: [PATCH 443/700] net: fec: make FIXED_PHY dependency unconditional When CONFIG_FIXED_PHY is in a loadable module, the fec driver cannot be built-in any more: x86_64-linux-ld: vmlinux.o: in function `fec_enet_mii_probe': fec_main.c:(.text+0xc4f367): undefined reference to `fixed_phy_unregister' x86_64-linux-ld: vmlinux.o: in function `fec_enet_close': fec_main.c:(.text+0xc59591): undefined reference to `fixed_phy_unregister' x86_64-linux-ld: vmlinux.o: in function `fec_enet_mii_probe.cold': Select the fixed phy support on all targets to make this build correctly, not just on coldfire. Notat that Essentially the stub helpers in include/linux/phy_fixed.h cannot be used correctly because of this build time dependency, and we could just remove them to hit the build failure more often when a driver uses them without the 'select FIXED_PHY'. Fixes: dc86b621e1b4 ("net: fec: register a fixed phy using fixed_phy_register_100fd if needed") Signed-off-by: Arnd Bergmann Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260402141048.2713445-1-arnd@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index e2a591cf9601f2..11edbb46a1180d 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -28,7 +28,7 @@ config FEC depends on PTP_1588_CLOCK_OPTIONAL select CRC32 select PHYLIB - select FIXED_PHY if M5272 + select FIXED_PHY select PAGE_POOL imply PAGE_POOL_STATS imply NET_SELFTESTS From b120e4432f9f56c7103133d6a11245e617695adb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 2 Apr 2026 10:35:19 +0000 Subject: [PATCH 444/700] net: lapbether: handle NETDEV_PRE_TYPE_CHANGE lapbeth_data_transmit() expects the underlying device type to be ARPHRD_ETHER. Returning NOTIFY_BAD from lapbeth_device_event() makes sure bonding driver can not break this expectation. Fixes: 872254dd6b1f ("net/bonding: Enable bonding to enslave non ARPHRD_ETHER") Reported-by: syzbot+d8c285748fa7292580a9@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/69cd22a1.050a0220.70c3a.0002.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Cc: Martin Schiller Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260402103519.1201565-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/wan/lapbether.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index f357a7ac70ac47..9861c99ea56c4e 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -446,33 +446,36 @@ static void lapbeth_free_device(struct lapbethdev *lapbeth) static int lapbeth_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct lapbethdev *lapbeth; struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct lapbethdev *lapbeth; if (dev_net(dev) != &init_net) return NOTIFY_DONE; - if (!dev_is_ethdev(dev) && !lapbeth_get_x25_dev(dev)) + lapbeth = lapbeth_get_x25_dev(dev); + if (!dev_is_ethdev(dev) && !lapbeth) return NOTIFY_DONE; switch (event) { case NETDEV_UP: /* New ethernet device -> new LAPB interface */ - if (!lapbeth_get_x25_dev(dev)) + if (!lapbeth) lapbeth_new_device(dev); break; case NETDEV_GOING_DOWN: /* ethernet device closes -> close LAPB interface */ - lapbeth = lapbeth_get_x25_dev(dev); if (lapbeth) dev_close(lapbeth->axdev); break; case NETDEV_UNREGISTER: /* ethernet device disappears -> remove LAPB interface */ - lapbeth = lapbeth_get_x25_dev(dev); if (lapbeth) lapbeth_free_device(lapbeth); break; + case NETDEV_PRE_TYPE_CHANGE: + /* Our underlying device type must not change. */ + if (lapbeth) + return NOTIFY_BAD; } return NOTIFY_DONE; From 285fa6b1e03cff78ead0383e1b259c44b95faf90 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 2 Apr 2026 14:57:10 +0200 Subject: [PATCH 445/700] net: airoha: Fix memory leak in airoha_qdma_rx_process() If an error occurs on the subsequents buffers belonging to the non-linear part of the skb (e.g. due to an error in the payload length reported by the NIC or if we consumed all the available fragments for the skb), the page_pool fragment will not be linked to the skb so it will not return to the pool in the airoha_qdma_rx_process() error path. Fix the memory leak partially reverting commit 'd6d2b0e1538d ("net: airoha: Fix page recycling in airoha_qdma_rx_process()")' and always running page_pool_put_full_page routine in the airoha_qdma_rx_process() error path. Fixes: d6d2b0e1538d ("net: airoha: Fix page recycling in airoha_qdma_rx_process()") Signed-off-by: Lorenzo Bianconi Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260402-airoha_qdma_rx_process-mem-leak-fix-v1-1-b5706f402d3c@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 95ba99b89428e4..91cb63a32d9904 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -697,9 +697,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) if (q->skb) { dev_kfree_skb(q->skb); q->skb = NULL; - } else { - page_pool_put_full_page(q->page_pool, page, true); } + page_pool_put_full_page(q->page_pool, page, true); } airoha_qdma_fill_rx_queue(q); From 4e65a8b8daa18d63255ec58964dd192c7fdd9f8b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 2 Apr 2026 10:17:32 +0000 Subject: [PATCH 446/700] ipv6: ioam: fix potential NULL dereferences in __ioam6_fill_trace_data() We need to check __in6_dev_get() for possible NULL value, as suggested by Yiming Qian. Also add skb_dst_dev_rcu() instead of skb_dst_dev(), and two missing READ_ONCE(). Note that @dev can't be NULL. Fixes: 9ee11f0fff20 ("ipv6: ioam: Data plane support for Pre-allocated Trace") Reported-by: Yiming Qian Signed-off-by: Eric Dumazet Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260402101732.1188059-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ioam6.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index 3978773bec4248..05a0b7d7e2aac3 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -710,7 +710,9 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, struct ioam6_schema *sc, unsigned int sclen, bool is_input) { - struct net_device *dev = skb_dst_dev(skb); + /* Note: skb_dst_dev_rcu() can't be NULL at this point. */ + struct net_device *dev = skb_dst_dev_rcu(skb); + struct inet6_dev *i_skb_dev, *idev; struct timespec64 ts; ktime_t tstamp; u64 raw64; @@ -721,13 +723,16 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4; + i_skb_dev = skb->dev ? __in6_dev_get(skb->dev) : NULL; + idev = __in6_dev_get(dev); + /* hop_lim and node_id */ if (trace->type.bit0) { byte = ipv6_hdr(skb)->hop_limit; if (is_input) byte--; - raw32 = dev_net(dev)->ipv6.sysctl.ioam6_id; + raw32 = READ_ONCE(dev_net(dev)->ipv6.sysctl.ioam6_id); *(__be32 *)data = cpu_to_be32((byte << 24) | raw32); data += sizeof(__be32); @@ -735,18 +740,18 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, /* ingress_if_id and egress_if_id */ if (trace->type.bit1) { - if (!skb->dev) + if (!i_skb_dev) raw16 = IOAM6_U16_UNAVAILABLE; else - raw16 = (__force u16)READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id); + raw16 = (__force u16)READ_ONCE(i_skb_dev->cnf.ioam6_id); *(__be16 *)data = cpu_to_be16(raw16); data += sizeof(__be16); - if (dev->flags & IFF_LOOPBACK) + if ((dev->flags & IFF_LOOPBACK) || !idev) raw16 = IOAM6_U16_UNAVAILABLE; else - raw16 = (__force u16)READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id); + raw16 = (__force u16)READ_ONCE(idev->cnf.ioam6_id); *(__be16 *)data = cpu_to_be16(raw16); data += sizeof(__be16); @@ -822,7 +827,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, if (is_input) byte--; - raw64 = dev_net(dev)->ipv6.sysctl.ioam6_id_wide; + raw64 = READ_ONCE(dev_net(dev)->ipv6.sysctl.ioam6_id_wide); *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64); data += sizeof(__be64); @@ -830,18 +835,18 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, /* ingress_if_id and egress_if_id (wide) */ if (trace->type.bit9) { - if (!skb->dev) + if (!i_skb_dev) raw32 = IOAM6_U32_UNAVAILABLE; else - raw32 = READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id_wide); + raw32 = READ_ONCE(i_skb_dev->cnf.ioam6_id_wide); *(__be32 *)data = cpu_to_be32(raw32); data += sizeof(__be32); - if (dev->flags & IFF_LOOPBACK) + if ((dev->flags & IFF_LOOPBACK) || !idev) raw32 = IOAM6_U32_UNAVAILABLE; else - raw32 = READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id_wide); + raw32 = READ_ONCE(idev->cnf.ioam6_id_wide); *(__be32 *)data = cpu_to_be32(raw32); data += sizeof(__be32); From 1979645e1842cb7017525a61a0e0e0beb924d02a Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Thu, 2 Apr 2026 07:01:53 -0700 Subject: [PATCH 447/700] bridge: guard local VLAN-0 FDB helpers against NULL vlan group When CONFIG_BRIDGE_VLAN_FILTERING is not set, br_vlan_group() and nbp_vlan_group() return NULL (br_private.h stub definitions). The BR_BOOLOPT_FDB_LOCAL_VLAN_0 toggle code is compiled unconditionally and reaches br_fdb_delete_locals_per_vlan_port() and br_fdb_insert_locals_per_vlan_port(), where the NULL vlan group pointer is dereferenced via list_for_each_entry(v, &vg->vlan_list, vlist). The observed crash is in the delete path, triggered when creating a bridge with IFLA_BR_MULTI_BOOLOPT containing BR_BOOLOPT_FDB_LOCAL_VLAN_0 via RTM_NEWLINK. The insert helper has the same bug pattern. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000056: 0000 [#1] KASAN NOPTI KASAN: null-ptr-deref in range [0x00000000000002b0-0x00000000000002b7] RIP: 0010:br_fdb_delete_locals_per_vlan+0x2b9/0x310 Call Trace: br_fdb_toggle_local_vlan_0+0x452/0x4c0 br_toggle_fdb_local_vlan_0+0x31/0x80 net/bridge/br.c:276 br_boolopt_toggle net/bridge/br.c:313 br_boolopt_multi_toggle net/bridge/br.c:364 br_changelink net/bridge/br_netlink.c:1542 br_dev_newlink net/bridge/br_netlink.c:1575 Add NULL checks for the vlan group pointer in both helpers, returning early when there are no VLANs to iterate. This matches the existing pattern used by other bridge FDB functions such as br_fdb_add() and br_fdb_delete(). Fixes: 21446c06b441 ("net: bridge: Introduce UAPI for BR_BOOLOPT_FDB_LOCAL_VLAN_0") Signed-off-by: Zijing Yin Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260402140153.3925663-1-yzjaurora@gmail.com Signed-off-by: Jakub Kicinski --- net/bridge/br_fdb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 0501ffcb8a3ddb..e2c17f620f009a 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -597,6 +597,9 @@ static void br_fdb_delete_locals_per_vlan_port(struct net_bridge *br, dev = br->dev; } + if (!vg) + return; + list_for_each_entry(v, &vg->vlan_list, vlist) br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid); } @@ -630,6 +633,9 @@ static int br_fdb_insert_locals_per_vlan_port(struct net_bridge *br, dev = br->dev; } + if (!vg) + return 0; + list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; From 7b735ef81286007794a227ce2539419479c02a5f Mon Sep 17 00:00:00 2001 From: Nikolaos Gkarlis Date: Thu, 2 Apr 2026 20:14:32 +0200 Subject: [PATCH 448/700] rtnetlink: add missing netlink_ns_capable() check for peer netns rtnl_newlink() lacks a CAP_NET_ADMIN capability check on the peer network namespace when creating paired devices (veth, vxcan, netkit). This allows an unprivileged user with a user namespace to create interfaces in arbitrary network namespaces, including init_net. Add a netlink_ns_capable() check for CAP_NET_ADMIN in the peer namespace before allowing device creation to proceed. Fixes: 81adee47dfb6 ("net: Support specifying the network namespace upon device creation.") Signed-off-by: Nikolaos Gkarlis Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260402181432.4126920-1-nickgarlis@gmail.com Signed-off-by: Jakub Kicinski --- net/core/rtnetlink.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index fae8034efbff0a..69daba3ddaf06b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3894,28 +3894,42 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm, goto out; } -static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops, +static struct net *rtnl_get_peer_net(struct sk_buff *skb, + const struct rtnl_link_ops *ops, struct nlattr *tbp[], struct nlattr *data[], struct netlink_ext_ack *extack) { - struct nlattr *tb[IFLA_MAX + 1]; + struct nlattr *tb[IFLA_MAX + 1], **attrs; + struct net *net; int err; - if (!data || !data[ops->peer_type]) - return rtnl_link_get_net_ifla(tbp); - - err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack); - if (err < 0) - return ERR_PTR(err); - - if (ops->validate) { - err = ops->validate(tb, NULL, extack); + if (!data || !data[ops->peer_type]) { + attrs = tbp; + } else { + err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack); if (err < 0) return ERR_PTR(err); + + if (ops->validate) { + err = ops->validate(tb, NULL, extack); + if (err < 0) + return ERR_PTR(err); + } + + attrs = tb; } - return rtnl_link_get_net_ifla(tb); + net = rtnl_link_get_net_ifla(attrs); + if (IS_ERR_OR_NULL(net)) + return net; + + if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); + return ERR_PTR(-EPERM); + } + + return net; } static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -4054,7 +4068,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, } if (ops->peer_type) { - peer_net = rtnl_get_peer_net(ops, tb, data, extack); + peer_net = rtnl_get_peer_net(skb, ops, tb, data, extack); if (IS_ERR(peer_net)) { ret = PTR_ERR(peer_net); goto put_ops; From 48a5fe38772b6f039522469ee6131a67838221a8 Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Thu, 2 Apr 2026 09:48:57 +0000 Subject: [PATCH 449/700] tipc: fix bc_ackers underflow on duplicate GRP_ACK_MSG The GRP_ACK_MSG handler in tipc_group_proto_rcv() currently decrements bc_ackers on every inbound group ACK, even when the same member has already acknowledged the current broadcast round. Because bc_ackers is a u16, a duplicate ACK received after the last legitimate ACK wraps the counter to 65535. Once wrapped, tipc_group_bc_cong() keeps reporting congestion and later group broadcasts on the affected socket stay blocked until the group is recreated. Fix this by ignoring duplicate or stale ACKs before touching bc_acked or bc_ackers. This makes repeated GRP_ACK_MSG handling idempotent and prevents the underflow path. Fixes: 2f487712b893 ("tipc: guarantee that group broadcast doesn't bypass group unicast") Cc: stable@vger.kernel.org Signed-off-by: Oleh Konko Reviewed-by: Tung Nguyen Reviewed-by: Simon Horman Link: https://patch.msgid.link/41a4833f368641218e444fdcff822039.security@1seal.org Signed-off-by: Jakub Kicinski --- net/tipc/group.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tipc/group.c b/net/tipc/group.c index e0e6227b433b75..14e6732624e28e 100644 --- a/net/tipc/group.c +++ b/net/tipc/group.c @@ -746,6 +746,7 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, u32 port = msg_origport(hdr); struct tipc_member *m, *pm; u16 remitted, in_flight; + u16 acked; if (!grp) return; @@ -798,7 +799,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, case GRP_ACK_MSG: if (!m) return; - m->bc_acked = msg_grp_bc_acked(hdr); + acked = msg_grp_bc_acked(hdr); + if (less_eq(acked, m->bc_acked)) + return; + m->bc_acked = acked; if (--grp->bc_ackers) return; list_del_init(&m->small_win); From b76254c55dc8f23edc089027dd3f8792554c69fb Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 15:12:07 +0800 Subject: [PATCH 450/700] net: qualcomm: qca_uart: report the consumed byte on RX skb allocation failure qca_tty_receive() consumes each input byte before checking whether a completed frame needs a fresh receive skb. When the current byte completes a frame, the driver delivers that frame and then allocates a new skb for the next one. If that allocation fails, the current code returns i even though data[i] has already been consumed and may already have completed the delivered frame. Since serdev interprets the return value as the number of accepted bytes, this under-reports progress by one byte and can replay the final byte of the completed frame into a fresh parser state on the next call. Return i + 1 in that failure path so the accepted-byte count matches the actual receive-state progress. Fixes: dfc768fbe618 ("net: qualcomm: add QCA7000 UART driver") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Stefan Wahren Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260402071207.4036-1-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qualcomm/qca_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 37efb1ea9fcd9c..847a5f928e4134 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -100,7 +100,7 @@ qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) if (!qca->rx_skb) { netdev_dbg(netdev, "recv: out of RX resources\n"); n_stats->rx_errors++; - return i; + return i + 1; } } } From 06aaf04ca815f7a1f17762fd847b7bc14b8833fb Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 2 Apr 2026 09:26:12 +0200 Subject: [PATCH 451/700] ipv4: nexthop: avoid duplicate NHA_HW_STATS_ENABLE on nexthop group dump Currently NHA_HW_STATS_ENABLE is included twice everytime a dump of nexthop group is performed with NHA_OP_FLAG_DUMP_STATS. As all the stats querying were moved to nla_put_nh_group_stats(), leave only that instance of the attribute querying. Fixes: 5072ae00aea4 ("net: nexthop: Expose nexthop group HW stats to user space") Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260402072613.25262-1-fmancera@suse.de Signed-off-by: Jakub Kicinski --- net/ipv4/nexthop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index c942f12822363c..a0c69458329940 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -902,8 +902,7 @@ static int nla_put_nh_group(struct sk_buff *skb, struct nexthop *nh, goto nla_put_failure; if (op_flags & NHA_OP_FLAG_DUMP_STATS && - (nla_put_u32(skb, NHA_HW_STATS_ENABLE, nhg->hw_stats) || - nla_put_nh_group_stats(skb, nh, op_flags))) + nla_put_nh_group_stats(skb, nh, op_flags)) goto nla_put_failure; return 0; From 14cf0cd35361f4e94824bf8a42f72713d7702a73 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 2 Apr 2026 09:26:13 +0200 Subject: [PATCH 452/700] ipv4: nexthop: allocate skb dynamically in rtm_get_nexthop() When querying a nexthop object via RTM_GETNEXTHOP, the kernel currently allocates a fixed-size skb using NLMSG_GOODSIZE. While sufficient for single nexthops and small Equal-Cost Multi-Path groups, this fixed allocation fails for large nexthop groups like 512 nexthops. This results in the following warning splat: WARNING: net/ipv4/nexthop.c:3395 at rtm_get_nexthop+0x176/0x1c0, CPU#20: rep/4608 [...] RIP: 0010:rtm_get_nexthop (net/ipv4/nexthop.c:3395) [...] Call Trace: rtnetlink_rcv_msg (net/core/rtnetlink.c:6989) netlink_rcv_skb (net/netlink/af_netlink.c:2550) netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1344) netlink_sendmsg (net/netlink/af_netlink.c:1894) ____sys_sendmsg (net/socket.c:721 net/socket.c:736 net/socket.c:2585) ___sys_sendmsg (net/socket.c:2641) __sys_sendmsg (net/socket.c:2671) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Fix this by allocating the size dynamically using nh_nlmsg_size() and using nlmsg_new(), this is consistent with nexthop_notify() behavior. In addition, adjust nh_nlmsg_size_grp() so it calculates the size needed based on flags passed. While at it, also add the size of NHA_FDB for nexthop group size calculation as it was missing too. This cannot be reproduced via iproute2 as the group size is currently limited and the command fails as follows: addattr_l ERROR: message exceeded bound of 1048 Fixes: 430a049190de ("nexthop: Add support for nexthop groups") Reported-by: Yiming Qian Closes: https://lore.kernel.org/netdev/CAL_bE8Li2h4KO+AQFXW4S6Yb_u5X4oSKnkywW+LPFjuErhqELA@mail.gmail.com/ Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260402072613.25262-2-fmancera@suse.de Signed-off-by: Jakub Kicinski --- net/ipv4/nexthop.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index a0c69458329940..2c9036c719b686 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1003,16 +1003,32 @@ static size_t nh_nlmsg_size_grp_res(struct nh_group *nhg) nla_total_size_64bit(8);/* NHA_RES_GROUP_UNBALANCED_TIME */ } -static size_t nh_nlmsg_size_grp(struct nexthop *nh) +static size_t nh_nlmsg_size_grp(struct nexthop *nh, u32 op_flags) { struct nh_group *nhg = rtnl_dereference(nh->nh_grp); size_t sz = sizeof(struct nexthop_grp) * nhg->num_nh; size_t tot = nla_total_size(sz) + - nla_total_size(2); /* NHA_GROUP_TYPE */ + nla_total_size(2) + /* NHA_GROUP_TYPE */ + nla_total_size(0); /* NHA_FDB */ if (nhg->resilient) tot += nh_nlmsg_size_grp_res(nhg); + if (op_flags & NHA_OP_FLAG_DUMP_STATS) { + tot += nla_total_size(0) + /* NHA_GROUP_STATS */ + nla_total_size(4); /* NHA_HW_STATS_ENABLE */ + tot += nhg->num_nh * + (nla_total_size(0) + /* NHA_GROUP_STATS_ENTRY */ + nla_total_size(4) + /* NHA_GROUP_STATS_ENTRY_ID */ + nla_total_size_64bit(8)); /* NHA_GROUP_STATS_ENTRY_PACKETS */ + + if (op_flags & NHA_OP_FLAG_DUMP_HW_STATS) { + tot += nhg->num_nh * + nla_total_size_64bit(8); /* NHA_GROUP_STATS_ENTRY_PACKETS_HW */ + tot += nla_total_size(4); /* NHA_HW_STATS_USED */ + } + } + return tot; } @@ -1047,14 +1063,14 @@ static size_t nh_nlmsg_size_single(struct nexthop *nh) return sz; } -static size_t nh_nlmsg_size(struct nexthop *nh) +static size_t nh_nlmsg_size(struct nexthop *nh, u32 op_flags) { size_t sz = NLMSG_ALIGN(sizeof(struct nhmsg)); sz += nla_total_size(4); /* NHA_ID */ if (nh->is_group) - sz += nh_nlmsg_size_grp(nh) + + sz += nh_nlmsg_size_grp(nh, op_flags) + nla_total_size(4) + /* NHA_OP_FLAGS */ 0; else @@ -1070,7 +1086,7 @@ static void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info) struct sk_buff *skb; int err = -ENOBUFS; - skb = nlmsg_new(nh_nlmsg_size(nh), gfp_any()); + skb = nlmsg_new(nh_nlmsg_size(nh, 0), gfp_any()); if (!skb) goto errout; @@ -3376,15 +3392,15 @@ static int rtm_get_nexthop(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (err) return err; - err = -ENOBUFS; - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - goto out; - err = -ENOENT; nh = nexthop_find_by_id(net, id); if (!nh) - goto errout_free; + goto out; + + err = -ENOBUFS; + skb = nlmsg_new(nh_nlmsg_size(nh, op_flags), GFP_KERNEL); + if (!skb) + goto out; err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 0, op_flags); From fde29fd9349327acc50d19a0b5f3d5a6c964dfd8 Mon Sep 17 00:00:00 2001 From: Yiqi Sun Date: Thu, 2 Apr 2026 15:04:19 +0800 Subject: [PATCH 453/700] ipv4: icmp: fix null-ptr-deref in icmp_build_probe() ipv6_stub->ipv6_dev_find() may return ERR_PTR(-EAFNOSUPPORT) when the IPv6 stack is not active (CONFIG_IPV6=m and not loaded), and passing this error pointer to dev_hold() will cause a kernel crash with null-ptr-deref. Instead, silently discard the request. RFC 8335 does not appear to define a specific response for the case where an IPv6 interface identifier is syntactically valid but the implementation cannot perform the lookup at runtime, and silently dropping the request may safer than misreporting "No Such Interface". Fixes: d329ea5bd884 ("icmp: add response to RFC 8335 PROBE messages") Signed-off-by: Yiqi Sun Link: https://patch.msgid.link/20260402070419.2291578-1-sunyiqixm@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv4/icmp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 568bd1e95d447f..4e2a6c70dcd840 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -1346,6 +1346,13 @@ bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr) if (iio->ident.addr.ctype3_hdr.addrlen != sizeof(struct in6_addr)) goto send_mal_query; dev = ipv6_stub->ipv6_dev_find(net, &iio->ident.addr.ip_addr.ipv6_addr, dev); + /* + * If IPv6 identifier lookup is unavailable, silently + * discard the request instead of misreporting NO_IF. + */ + if (IS_ERR(dev)) + return false; + dev_hold(dev); break; #endif From 77facb35227c421467cdb49268de433168c2dcef Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Thu, 2 Apr 2026 17:23:16 -0500 Subject: [PATCH 454/700] net: increase IP_TUNNEL_RECURSION_LIMIT to 5 In configurations with multiple tunnel layers and MPLS lwtunnel routing, a single tunnel hop can increment the counter beyond this limit. This causes packets to be dropped with the "Dead loop on virtual device" message even when a routing loop doesn't exist. Increase IP_TUNNEL_RECURSION_LIMIT from 4 to 5 to handle this use-case. Fixes: 6f1a9140ecda ("net: add xmit recursion limit to tunnel xmit functions") Link: https://lore.kernel.org/netdev/88deb91b-ef1b-403c-8eeb-0f971f27e34f@redhat.com/ Signed-off-by: Chris J Arges Link: https://patch.msgid.link/20260402222401.3408368-1-carges@cloudflare.com Signed-off-by: Jakub Kicinski --- include/net/ip_tunnels.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 1f577a4f8ce9b1..d708b66e55cda5 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -32,7 +32,7 @@ * recursion involves route lookups and full IP output, consuming much * more stack per level, so a lower limit is needed. */ -#define IP_TUNNEL_RECURSION_LIMIT 4 +#define IP_TUNNEL_RECURSION_LIMIT 5 /* Keep error state on tunnel for 30 sec */ #define IPTUNNEL_ERR_TIMEO (30*HZ) From 5c14a19d5b1645cce1cb1252833d70b23635b632 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 12:21:48 +0800 Subject: [PATCH 455/700] nfc: s3fwrn5: allocate rx skb before consuming bytes s3fwrn82_uart_read() reports the number of accepted bytes to the serdev core. The current code consumes bytes into recv_skb and may already deliver a complete frame before allocating a fresh receive buffer. If that alloc_skb() fails, the callback returns 0 even though it has already consumed bytes, and it leaves recv_skb as NULL for the next receive callback. That breaks the receive_buf() accounting contract and can also lead to a NULL dereference on the next skb_put_u8(). Allocate the receive skb lazily before consuming the next byte instead. If allocation fails, return the number of bytes already accepted. Fixes: 3f52c2cb7e3a ("nfc: s3fwrn5: Support a UART interface") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260402042148.65236-1-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/nfc/s3fwrn5/uart.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 9c09c10c2a4640..4ee481bd7e9654 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -58,6 +58,12 @@ static size_t s3fwrn82_uart_read(struct serdev_device *serdev, size_t i; for (i = 0; i < count; i++) { + if (!phy->recv_skb) { + phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); + if (!phy->recv_skb) + return i; + } + skb_put_u8(phy->recv_skb, *data++); if (phy->recv_skb->len < S3FWRN82_NCI_HEADER) @@ -69,9 +75,7 @@ static size_t s3fwrn82_uart_read(struct serdev_device *serdev, s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb, phy->common.mode); - phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); - if (!phy->recv_skb) - return 0; + phy->recv_skb = NULL; } return i; From 1345e9f4e3f3bc7d8a0a2138ae29e205a857a555 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 1 Apr 2026 11:29:39 +0100 Subject: [PATCH 456/700] net: stmmac: Fix PTP ref clock for Tegra234 Since commit 030ce919e114 ("net: stmmac: make sure that ptp_rate is not 0 before configuring timestamping") was added the following error is observed on Tegra234: ERR KERN tegra-mgbe 6800000.ethernet eth0: Invalid PTP clock rate WARNING KERN tegra-mgbe 6800000.ethernet eth0: PTP init failed It turns out that the Tegra234 device-tree binding defines the PTP ref clock name as 'ptp-ref' and not 'ptp_ref' and the above commit now exposes this and that the PTP clock is not configured correctly. In order to update device-tree to use the correct 'ptp_ref' name, update the Tegra MGBE driver to use 'ptp_ref' by default and fallback to using 'ptp-ref' if this clock name is present. Fixes: d8ca113724e7 ("net: stmmac: tegra: Add MGBE support") Signed-off-by: Jon Hunter Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260401102941.17466-2-jonathanh@nvidia.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/stmicro/stmmac/dwmac-tegra.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c index d765acbe375481..21a0a11fc0118b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -9,7 +9,7 @@ #include "stmmac_platform.h" static const char *const mgbe_clks[] = { - "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac" + "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp_ref", "mac" }; struct tegra_mgbe { @@ -215,6 +215,7 @@ static int tegra_mgbe_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat; struct stmmac_resources res; + bool use_legacy_ptp = false; struct tegra_mgbe *mgbe; int irq, err, i; u32 value; @@ -257,9 +258,23 @@ static int tegra_mgbe_probe(struct platform_device *pdev) if (!mgbe->clks) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) + /* Older device-trees use 'ptp-ref' rather than 'ptp_ref'. + * Fall back when the legacy name is present. + */ + if (of_property_match_string(pdev->dev.of_node, "clock-names", + "ptp-ref") >= 0) + use_legacy_ptp = true; + + for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) { mgbe->clks[i].id = mgbe_clks[i]; + if (use_legacy_ptp && !strcmp(mgbe_clks[i], "ptp_ref")) { + dev_warn(mgbe->dev, + "Device-tree update needed for PTP clock!\n"); + mgbe->clks[i].id = "ptp-ref"; + } + } + err = devm_clk_bulk_get(mgbe->dev, ARRAY_SIZE(mgbe_clks), mgbe->clks); if (err < 0) return err; From fb22b1fc5bca3c0aad95388933497ceb30f1fb26 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 1 Apr 2026 11:29:40 +0100 Subject: [PATCH 457/700] dt-bindings: net: Fix Tegra234 MGBE PTP clock The PTP clock for the Tegra234 MGBE device is incorrectly named 'ptp-ref' and should be 'ptp_ref'. This is causing the following warning to be observed on Tegra234 platforms that use this device: ERR KERN tegra-mgbe 6800000.ethernet eth0: Invalid PTP clock rate WARNING KERN tegra-mgbe 6800000.ethernet eth0: PTP init failed Although this constitutes an ABI breakage in the binding for this device, PTP support has clearly never worked and so fix this now so we can correct the device-tree for this device. Note that the MGBE driver still supports the legacy 'ptp-ref' clock name and so older/existing device-trees will still work, but given that this is not the correct name, there is no point to advertise this in the binding. Fixes: 189c2e5c7669 ("dt-bindings: net: Add Tegra234 MGBE") Signed-off-by: Jon Hunter Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260401102941.17466-3-jonathanh@nvidia.com Signed-off-by: Jakub Kicinski --- .../devicetree/bindings/net/nvidia,tegra234-mgbe.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml index 2bd3efff2485ef..215f14d1897d21 100644 --- a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml +++ b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml @@ -42,7 +42,7 @@ properties: - const: mgbe - const: mac - const: mac-divider - - const: ptp-ref + - const: ptp_ref - const: rx-input-m - const: rx-input - const: tx @@ -133,7 +133,7 @@ examples: <&bpmp TEGRA234_CLK_MGBE0_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE0_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE0_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE0_MAC>, From b6422dff0e518245019233432b6bccfc30b73e2f Mon Sep 17 00:00:00 2001 From: Sahil Chandna Date: Fri, 3 Apr 2026 05:09:29 -0700 Subject: [PATCH 458/700] PCI: hv: Fix double ida_free in hv_pci_probe error path If hv_pci_probe() fails after storing the domain number in hbus->bridge->domain_nr, there is a call to free this domain_nr via pci_bus_release_emul_domain_nr(), however, during cleanup, the bridge release callback pci_release_host_bridge_dev() also frees the domain_nr causing ida_free to be called on same ID twice and triggering following warning: ida_free called for id=28971 which is not allocated. WARNING: lib/idr.c:594 at ida_free+0xdf/0x160, CPU#0: kworker/0:2/198 Call Trace: pci_bus_release_emul_domain_nr+0x17/0x20 pci_release_host_bridge_dev+0x4b/0x60 device_release+0x3b/0xa0 kobject_put+0x8e/0x220 devm_pci_alloc_host_bridge_release+0xe/0x20 devres_release_all+0x9a/0xd0 device_unbind_cleanup+0x12/0xa0 really_probe+0x1c5/0x3f0 vmbus_add_channel_work+0x135/0x1a0 Fix this by letting pci core handle the free domain_nr and remove the explicit free called in pci-hyperv driver. Fixes: bcce8c74f1ce ("PCI: Enable host bridge emulation for PCI_DOMAINS_GENERIC platforms") Signed-off-by: Sahil Chandna Reviewed-by: Manivannan Sadhasivam Reviewed-by: Saurabh Sengar Signed-off-by: Wei Liu --- drivers/pci/controller/pci-hyperv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 38a790f642a1ea..49c0a2d51162a2 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -3786,7 +3786,7 @@ static int hv_pci_probe(struct hv_device *hdev, hbus->bridge->domain_nr); if (!hbus->wq) { ret = -ENOMEM; - goto free_dom; + goto free_bus; } hdev->channel->next_request_id_callback = vmbus_next_request_id; @@ -3882,8 +3882,6 @@ static int hv_pci_probe(struct hv_device *hdev, vmbus_close(hdev->channel); destroy_wq: destroy_workqueue(hbus->wq); -free_dom: - pci_bus_release_emul_domain_nr(hbus->bridge->domain_nr); free_bus: kfree(hbus); return ret; From 16cbec24897624051b324aa3a85859c38ca65fde Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Tue, 24 Mar 2026 23:57:40 +0000 Subject: [PATCH 459/700] mshv: Fix infinite fault loop on permission-denied GPA intercepts Prevent infinite fault loops when guests access memory regions without proper permissions. Currently, mshv_handle_gpa_intercept() attempts to remap pages for all faults on movable memory regions, regardless of whether the access type is permitted. When a guest writes to a read-only region, the remap succeeds but the region remains read-only, causing immediate re-fault and spinning the vCPU indefinitely. Validate intercept access type against region permissions before attempting remaps. Reject writes to non-writable regions and executes to non-executable regions early, returning false to let the VMM handle the intercept appropriately. This also closes a potential DoS vector where malicious guests could intentionally trigger these fault loops to consume host resources. Fixes: b9a66cd5ccbb ("mshv: Add support for movable memory regions") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 15 ++++++++++++--- include/hyperv/hvgdk_mini.h | 6 ++++++ include/hyperv/hvhdk.h | 4 ++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 6f42423f7faa8c..c8e5523a52b6b7 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -630,7 +630,7 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) { struct mshv_partition *p = vp->vp_partition; struct mshv_mem_region *region; - bool ret; + bool ret = false; u64 gfn; #if defined(CONFIG_X86_64) struct hv_x64_memory_intercept_message *msg = @@ -641,6 +641,8 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) (struct hv_arm64_memory_intercept_message *) vp->vp_intercept_msg_page->u.payload; #endif + enum hv_intercept_access_type access_type = + msg->header.intercept_access_type; gfn = HVPFN_DOWN(msg->guest_physical_address); @@ -648,12 +650,19 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) if (!region) return false; + if (access_type == HV_INTERCEPT_ACCESS_WRITE && + !(region->hv_map_flags & HV_MAP_GPA_WRITABLE)) + goto put_region; + + if (access_type == HV_INTERCEPT_ACCESS_EXECUTE && + !(region->hv_map_flags & HV_MAP_GPA_EXECUTABLE)) + goto put_region; + /* Only movable memory ranges are supported for GPA intercepts */ if (region->mreg_type == MSHV_REGION_TYPE_MEM_MOVABLE) ret = mshv_region_handle_gfn_fault(region, gfn); - else - ret = false; +put_region: mshv_region_put(region); return ret; diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 1823a290a7b7a7..f9600f87186ac8 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -1533,4 +1533,10 @@ struct hv_mmio_write_input { u8 data[HV_HYPERCALL_MMIO_MAX_DATA_LENGTH]; } __packed; +enum hv_intercept_access_type { + HV_INTERCEPT_ACCESS_READ = 0, + HV_INTERCEPT_ACCESS_WRITE = 1, + HV_INTERCEPT_ACCESS_EXECUTE = 2 +}; + #endif /* _HV_HVGDK_MINI_H */ diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index 245f3db53bf195..5e83d37149662c 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -779,7 +779,7 @@ struct hv_x64_intercept_message_header { u32 vp_index; u8 instruction_length:4; u8 cr8:4; /* Only set for exo partitions */ - u8 intercept_access_type; + u8 intercept_access_type; /* enum hv_intercept_access_type */ union hv_x64_vp_execution_state execution_state; struct hv_x64_segment_register cs_segment; u64 rip; @@ -825,7 +825,7 @@ union hv_arm64_vp_execution_state { struct hv_arm64_intercept_message_header { u32 vp_index; u8 instruction_length; - u8 intercept_access_type; + u8 intercept_access_type; /* enum hv_intercept_access_type */ union hv_arm64_vp_execution_state execution_state; u64 pc; u64 cpsr; From e2b0ae529db4766584e77647cefe3ec15c3d842e Mon Sep 17 00:00:00 2001 From: Zoltan Illes Date: Fri, 3 Apr 2026 22:03:42 -0700 Subject: [PATCH 460/700] Input: xpad - add support for Razer Wolverine V3 Pro Add device IDs for the Razer Wolverine V3 Pro controller in both wired (0x0a57) and wireless 2.4 GHz dongle (0x0a59) modes. The controller uses the Xbox 360 protocol (vendor-specific class, subclass 93, protocol 1) on interface 0 with an identical 20-byte input report layout, so no additional processing is needed. Signed-off-by: Zoltan Illes Link: https://patch.msgid.link/20260329220031.1325509-1-137647604+ZlordHUN@users.noreply.github.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 363d509493866a..655226fe493d79 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -313,6 +313,8 @@ static const struct xpad_device { { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x1532, 0x0a29, "Razer Wolverine V2", 0, XTYPE_XBOXONE }, + { 0x1532, 0x0a57, "Razer Wolverine V3 Pro (Wired)", 0, XTYPE_XBOX360 }, + { 0x1532, 0x0a59, "Razer Wolverine V3 Pro (2.4 GHz Dongle)", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, From 0d9363a764d9d601a05591f9695cea8b429e9be3 Mon Sep 17 00:00:00 2001 From: Shengyu Qu Date: Fri, 3 Apr 2026 22:07:28 -0700 Subject: [PATCH 461/700] Input: xpad - add support for BETOP BTP-KP50B/C controller's wireless mode BETOP's BTP-KP50B and BTP-KP50C controller's wireless dongles are both working as standard Xbox 360 controllers. Add USB device IDs for them to xpad driver. Signed-off-by: Shengyu Qu Link: https://patch.msgid.link/TY4PR01MB14432B4B298EA186E5F86C46B9855A@TY4PR01MB14432.jpnprd01.prod.outlook.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 655226fe493d79..627e8950e451e5 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -362,6 +362,8 @@ static const struct xpad_device { { 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 }, { 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 }, { 0x1ee9, 0x1590, "ZOTAC Gaming Zone", 0, XTYPE_XBOX360 }, + { 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 }, + { 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 }, { 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2064, "PowerA Wired Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, @@ -564,6 +566,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x1a86), /* Nanjing Qinheng Microelectronics (WCH) */ XPAD_XBOX360_VENDOR(0x1bad), /* Harmonix Rock Band guitar and drums */ XPAD_XBOX360_VENDOR(0x1ee9), /* ZOTAC Technology Limited */ + XPAD_XBOX360_VENDOR(0x20bc), /* BETOP wireless dongles */ XPAD_XBOX360_VENDOR(0x20d6), /* PowerA controllers */ XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA controllers */ XPAD_XBOX360_VENDOR(0x2345), /* Machenike Controllers */ From f387e2e2b9d302688dbdceebe9aade221c90f09e Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 4 Apr 2026 10:20:33 +0200 Subject: [PATCH 462/700] Documentation: fix two typos in latest update to the security report howto In previous patch "Documentation: clarify the mandatory and desirable info for security reports" I left two typos that I didn't detect in local checks. One is "get_maintainers.pl" (no 's' in the script name), and the other one is a missing closing quote after "Reported-by", which didn't have effect here but I don't know if it can break rendering elsewhere (e.g. on the public HTML page). Better fix it before it gets merged. Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260404082033.5160-1-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 0b1f6d8e3cbe71..27b028e8586104 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -59,7 +59,7 @@ In addition, the following information are highly desirable: immediately merged (see Documentation/process/submitting-patches.rst). This will save some back-and-forth exchanges if it is accepted, and you will be credited for finding and fixing this issue. Note that in this case - only a ``Signed-off-by:`` tag is needed, without ``Reported-by:` when the + only a ``Signed-off-by:`` tag is needed, without ``Reported-by:`` when the reporter and author are the same. * **mitigations**: very often during a bug analysis, some ways of mitigating @@ -88,7 +88,7 @@ recipients to send a report to. In the Linux kernel, all official maintainers are trusted, so the consequences of accidentally including the wrong maintainer are essentially a bit more noise for that person, i.e. nothing dramatic. As such, a suitable method to figure the list of maintainers (which kernel -security officers use) is to rely on the get_maintainers.pl script, tuned to +security officers use) is to rely on the get_maintainer.pl script, tuned to only report maintainers. This script, when passed a file name, will look for its path in the MAINTAINERS file to figure a hierarchical list of relevant maintainers. Calling it a first time with the finest level of filtering will From 834911eb8eef2501485d819b4eabebadc25c3497 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 23 Mar 2026 17:43:47 -0600 Subject: [PATCH 463/700] riscv: kgdb: fix several debug register assignment bugs Fix several bugs in the RISC-V kgdb implementation: - The element of dbg_reg_def[] that is supposed to pertain to the S1 register embeds instead the struct pt_regs offset of the A1 register. Fix this to use the S1 register offset in struct pt_regs. - The sleeping_thread_to_gdb_regs() function copies the value of the S10 register into the gdb_regs[] array element meant for the S9 register, and copies the value of the S11 register into the array element meant for the S10 register. It also neglects to copy the value of the S11 register. Fix all of these issues. Fixes: fe89bd2be8667 ("riscv: Add KGDB support") Cc: Vincent Chen Link: https://patch.msgid.link/fde376f8-bcfd-bfe4-e467-07d8f7608d05@kernel.org Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kgdb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c index 15fec5d1e6decf..0bf629204c76a4 100644 --- a/arch/riscv/kernel/kgdb.c +++ b/arch/riscv/kernel/kgdb.c @@ -175,7 +175,7 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { {DBG_REG_T1, GDB_SIZEOF_REG, offsetof(struct pt_regs, t1)}, {DBG_REG_T2, GDB_SIZEOF_REG, offsetof(struct pt_regs, t2)}, {DBG_REG_FP, GDB_SIZEOF_REG, offsetof(struct pt_regs, s0)}, - {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, + {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, s1)}, {DBG_REG_A0, GDB_SIZEOF_REG, offsetof(struct pt_regs, a0)}, {DBG_REG_A1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, {DBG_REG_A2, GDB_SIZEOF_REG, offsetof(struct pt_regs, a2)}, @@ -244,8 +244,9 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) gdb_regs[DBG_REG_S6_OFF] = task->thread.s[6]; gdb_regs[DBG_REG_S7_OFF] = task->thread.s[7]; gdb_regs[DBG_REG_S8_OFF] = task->thread.s[8]; - gdb_regs[DBG_REG_S9_OFF] = task->thread.s[10]; - gdb_regs[DBG_REG_S10_OFF] = task->thread.s[11]; + gdb_regs[DBG_REG_S9_OFF] = task->thread.s[9]; + gdb_regs[DBG_REG_S10_OFF] = task->thread.s[10]; + gdb_regs[DBG_REG_S11_OFF] = task->thread.s[11]; gdb_regs[DBG_REG_EPC_OFF] = task->thread.ra; } From 6b60a128c2f43180664a614830f3c529497e0394 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 23 Mar 2026 17:43:47 -0600 Subject: [PATCH 464/700] riscv: patch: Avoid early phys_to_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to commit 8d09e2d569f6 ("arm64: patching: avoid early page_to_phys()"), avoid using phys_to_page() for the kernel address case in patch_map(). Since this is called from apply_boot_alternatives() in setup_arch(), and commit 4267739cabb8 ("arch, mm: consolidate initialization of SPARSE memory model") has moved sparse_init() to after setup_arch(), phys_to_page() is not available there yet, and it panics on boot with SPARSEMEM on RV32, which does not use SPARSEMEM_VMEMMAP. Reported-by: Thomas Weißschuh Closes: https://lore.kernel.org/r/20260223144108-dcace0b9-02e8-4b67-a7ce-f263bed36f26@linutronix.de/ Fixes: 4267739cabb8 ("arch, mm: consolidate initialization of SPARSE memory model") Suggested-by: Mike Rapoport Signed-off-by: Vivian Wang Acked-by: Mike Rapoport (Microsoft) Tested-by: Thomas Weißschuh Link: https://patch.msgid.link/20260310-riscv-sparsemem-alternatives-fix-v1-1-659d5dd257e2@iscas.ac.cn [pjw@kernel.org: fix the subject line to align with the patch description] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/patch.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c index db13c9ddf9e3d4..16b243376f3621 100644 --- a/arch/riscv/kernel/patch.c +++ b/arch/riscv/kernel/patch.c @@ -42,19 +42,20 @@ static inline bool is_kernel_exittext(uintptr_t addr) static __always_inline void *patch_map(void *addr, const unsigned int fixmap) { uintptr_t uintaddr = (uintptr_t) addr; - struct page *page; + phys_addr_t phys; - if (core_kernel_text(uintaddr) || is_kernel_exittext(uintaddr)) - page = phys_to_page(__pa_symbol(addr)); - else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) - page = vmalloc_to_page(addr); - else - return addr; + if (core_kernel_text(uintaddr) || is_kernel_exittext(uintaddr)) { + phys = __pa_symbol(addr); + } else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) { + struct page *page = vmalloc_to_page(addr); - BUG_ON(!page); + BUG_ON(!page); + phys = page_to_phys(page) + offset_in_page(addr); + } else { + return addr; + } - return (void *)set_fixmap_offset(fixmap, page_to_phys(page) + - offset_in_page(addr)); + return (void *)set_fixmap_offset(fixmap, phys); } static void patch_unmap(int fixmap) From 57f0253bc1538446ee46a4550fe85d91235fb678 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 21 Feb 2026 10:37:31 +0800 Subject: [PATCH 465/700] riscv: make runtime const not usable by modules Similar as commit 284922f4c563 ("x86: uaccess: don't use runtime-const rewriting in modules") does, make riscv's runtime const not usable by modules too, to "make sure this doesn't get forgotten the next time somebody wants to do runtime constant optimizations". The reason is well explained in the above commit: "The runtime-const infrastructure was never designed to handle the modular case, because the constant fixup is only done at boot time for core kernel code." Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260221023731.3476-1-jszhang@kernel.org Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/runtime-const.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/include/asm/runtime-const.h b/arch/riscv/include/asm/runtime-const.h index d766e2b9e6df1b..900db0a103d058 100644 --- a/arch/riscv/include/asm/runtime-const.h +++ b/arch/riscv/include/asm/runtime-const.h @@ -2,6 +2,10 @@ #ifndef _ASM_RISCV_RUNTIME_CONST_H #define _ASM_RISCV_RUNTIME_CONST_H +#ifdef MODULE + #error "Cannot use runtime-const infrastructure from modules" +#endif + #include #include #include From 3033b2b1e3949274f33a140e2a97571b5a307298 Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Mon, 23 Mar 2026 00:00:22 +0800 Subject: [PATCH 466/700] riscv: Reset pmm when PR_TAGGED_ADDR_ENABLE is not set In set_tagged_addr_ctrl(), when PR_TAGGED_ADDR_ENABLE is not set, pmlen is correctly set to 0, but it forgets to reset pmm. This results in the CPU pmm state not corresponding to the software pmlen state. Fix this by resetting pmm along with pmlen. Fixes: 2e1743085887 ("riscv: Add support for the tagged address ABI") Signed-off-by: Zishun Yi Reviewed-by: Samuel Holland Link: https://patch.msgid.link/20260322160022.21908-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/process.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index aacb23978f9313..5957effab57c1d 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -347,8 +347,10 @@ long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen)) return -EINVAL; - if (!(arg & PR_TAGGED_ADDR_ENABLE)) + if (!(arg & PR_TAGGED_ADDR_ENABLE)) { pmlen = PMLEN_0; + pmm = ENVCFG_PMM_PMLEN_0; + } if (mmap_write_lock_killable(mm)) return -EINTR; From 87ad7cc9aa7f0a202189640c5015aa985e7e8f3b Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 2 Apr 2026 17:18:03 -0600 Subject: [PATCH 467/700] riscv: use _BITUL macro rather than BIT() in ptrace uapi and kselftests Fix the build of non-kernel code that includes the RISC-V ptrace uapi header, and the RISC-V validate_v_ptrace.c kselftest, by using the _BITUL() macro rather than BIT(). BIT() is not available outside the kernel. Based on patches and comments from Charlie Jenkins, Michael Neuling, and Andreas Schwab. Fixes: 30eb191c895b ("selftests: riscv: verify ptrace rejects invalid vector csr inputs") Fixes: 2af7c9cf021c ("riscv/ptrace: expose riscv CFI status and state via ptrace and in core files") Cc: Andreas Schwab Cc: Michael Neuling Cc: Charlie Jenkins Link: https://patch.msgid.link/20260330024248.449292-1-mikey@neuling.org Link: https://lore.kernel.org/linux-riscv/20260309-fix_selftests-v2-1-9d5a553a531e@gmail.com/ Link: https://lore.kernel.org/linux-riscv/20260309-fix_selftests-v2-3-9d5a553a531e@gmail.com/ Signed-off-by: Paul Walmsley --- arch/riscv/include/uapi/asm/ptrace.h | 13 +++++++------ .../selftests/riscv/vector/validate_v_ptrace.c | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 18988a5f1a630b..70a74adad91437 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -9,6 +9,7 @@ #ifndef __ASSEMBLER__ #include +#include #define PTRACE_GETFDPIC 33 @@ -138,12 +139,12 @@ struct __sc_riscv_cfi_state { #define PTRACE_CFI_SS_LOCK_BIT 4 #define PTRACE_CFI_SS_PTR_BIT 5 -#define PTRACE_CFI_LP_EN_STATE BIT(PTRACE_CFI_LP_EN_BIT) -#define PTRACE_CFI_LP_LOCK_STATE BIT(PTRACE_CFI_LP_LOCK_BIT) -#define PTRACE_CFI_ELP_STATE BIT(PTRACE_CFI_ELP_BIT) -#define PTRACE_CFI_SS_EN_STATE BIT(PTRACE_CFI_SS_EN_BIT) -#define PTRACE_CFI_SS_LOCK_STATE BIT(PTRACE_CFI_SS_LOCK_BIT) -#define PTRACE_CFI_SS_PTR_STATE BIT(PTRACE_CFI_SS_PTR_BIT) +#define PTRACE_CFI_LP_EN_STATE _BITUL(PTRACE_CFI_LP_EN_BIT) +#define PTRACE_CFI_LP_LOCK_STATE _BITUL(PTRACE_CFI_LP_LOCK_BIT) +#define PTRACE_CFI_ELP_STATE _BITUL(PTRACE_CFI_ELP_BIT) +#define PTRACE_CFI_SS_EN_STATE _BITUL(PTRACE_CFI_SS_EN_BIT) +#define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) +#define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) #define PRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ PTRACE_CFI_LP_LOCK_STATE | \ diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index 3589549f722891..7ae6fede496f6f 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -346,8 +346,8 @@ FIXTURE_TEARDOWN(v_csr_invalid) { } -#define VECTOR_1_0 BIT(0) -#define XTHEAD_VECTOR_0_7 BIT(1) +#define VECTOR_1_0 _BITUL(0) +#define XTHEAD_VECTOR_0_7 _BITUL(1) #define vector_test(x) ((x) & VECTOR_1_0) #define xthead_test(x) ((x) & XTHEAD_VECTOR_0_7) From 511361fe7a8856e2f415010942808c237a1b8061 Mon Sep 17 00:00:00 2001 From: Charlie Jenkins Date: Mon, 9 Mar 2026 18:52:11 -0700 Subject: [PATCH 468/700] selftests: riscv: Add braces around EXPECT_EQ() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EXPECT_EQ() expands to multiple lines, breaking up one-line if statements. This issue was not present in the patch on the mailing list but was instead introduced by the maintainer when attempting to fix up checkpatch warnings. Add braces around EXPECT_EQ() to avoid the error even though checkpatch suggests them to be removed: validate_v_ptrace.c:626:17: error: ‘else’ without a previous ‘if’ Fixes: 3789d5eecd5a ("selftests: riscv: verify syscalls discard vector context") Fixes: 30eb191c895b ("selftests: riscv: verify ptrace rejects invalid vector csr inputs") Fixes: 849f05ae1ea6 ("selftests: riscv: verify ptrace accepts valid vector csr values") Signed-off-by: Charlie Jenkins Reviewed-and-tested-by: Sergey Matyukevich Link: https://patch.msgid.link/20260309-fix_selftests-v2-2-9d5a553a531e@gmail.com Signed-off-by: Paul Walmsley --- .../selftests/riscv/vector/validate_v_ptrace.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c index 7ae6fede496f6f..74b6f6bcf06761 100644 --- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c +++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c @@ -290,10 +290,11 @@ TEST(ptrace_v_syscall_clobbering) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); @@ -619,10 +620,11 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); @@ -827,10 +829,11 @@ TEST_F(v_csr_valid, ptrace_v_valid_values) /* verify initial vsetvli settings */ - if (is_xtheadvector_supported()) + if (is_xtheadvector_supported()) { EXPECT_EQ(5UL, regset_data->vtype); - else + } else { EXPECT_EQ(9UL, regset_data->vtype); + } EXPECT_EQ(regset_data->vlenb, regset_data->vl); EXPECT_EQ(vlenb, regset_data->vlenb); From 9156585280f161fc1c3552cf1860559edb2bb7e3 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Tue, 3 Mar 2026 11:46:05 +0530 Subject: [PATCH 469/700] ACPI: RIMT: Add dependency between iommu and devices EPROBE_DEFER ensures IOMMU devices are probed before the devices that depend on them. During shutdown, however, the IOMMU may be removed first, leading to issues. To avoid this, a device link is added which enforces the correct removal order. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Sunil V L Link: https://patch.msgid.link/20260303061605.722949-1-sunilvl@oss.qualcomm.com Signed-off-by: Paul Walmsley --- drivers/acpi/riscv/rimt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 229c4a0d47a3ff..906282b0e63c0c 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -263,6 +263,13 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 if (!rimt_fwnode) return -EPROBE_DEFER; + /* + * EPROBE_DEFER ensures IOMMU is probed before the devices that + * depend on them. During shutdown, however, the IOMMU may be removed + * first, leading to issues. To avoid this, a device link is added + * which enforces the correct removal order. + */ + device_link_add(dev, rimt_fwnode->dev, DL_FLAG_AUTOREMOVE_CONSUMER); return acpi_iommu_fwspec_init(dev, deviceid, rimt_fwnode); } From a621d9cdc8d08bd2fe8dfe6fa6897d256de8248f Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 4 Apr 2026 18:40:57 -0600 Subject: [PATCH 470/700] riscv: ptrace: cfi: fix "PRACE" typo in uapi header A CFI-related macro defined in arch/riscv/uapi/asm/ptrace.h misspells "PTRACE" as "PRACE"; fix this. Fixes: 2af7c9cf021c ("riscv/ptrace: expose riscv CFI status and state via ptrace and in core files") Cc: Deepak Gupta Signed-off-by: Paul Walmsley --- arch/riscv/include/uapi/asm/ptrace.h | 2 +- arch/riscv/kernel/ptrace.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 70a74adad91437..69d71443e111e8 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -146,7 +146,7 @@ struct __sc_riscv_cfi_state { #define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) #define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) -#define PRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ +#define PTRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ PTRACE_CFI_LP_LOCK_STATE | \ PTRACE_CFI_ELP_STATE | \ PTRACE_CFI_SS_EN_STATE | \ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index e592bd6b766518..2704a532e91645 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -351,7 +351,7 @@ static int riscv_cfi_set(struct task_struct *target, if ((user_cfi.cfi_status.cfi_state & (PTRACE_CFI_LP_EN_STATE | PTRACE_CFI_LP_LOCK_STATE | PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) || - (user_cfi.cfi_status.cfi_state & PRACE_CFI_STATE_INVALID_MASK)) + (user_cfi.cfi_status.cfi_state & PTRACE_CFI_STATE_INVALID_MASK)) return -EINVAL; /* If lpad is enabled on target and ptrace requests to set / clear elp, do that */ From a6ede084c4b7cd6ecd0d31d5292336e556901bd7 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Sat, 4 Apr 2026 18:40:58 -0600 Subject: [PATCH 471/700] riscv: cfi: clear CFI lock status in start_thread() When libc locks the CFI status through the following prctl: - PR_LOCK_SHADOW_STACK_STATUS - PR_LOCK_INDIR_BR_LP_STATUS A newly execd address space will inherit the lock status if it does not clear the lock bits. Since the lock bits remain set, libc will later fail to enable the landing pad and shadow stack. Signed-off-by: Zong Li Link: https://patch.msgid.link/20260323065640.4045713-1-zong.li@sifive.com [pjw@kernel.org: ensure we unlock before changing state; cleaned up subject line] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/usercfi.h | 8 ++++---- arch/riscv/kernel/process.c | 2 ++ arch/riscv/kernel/usercfi.c | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/arch/riscv/include/asm/usercfi.h b/arch/riscv/include/asm/usercfi.h index 7495baae1e3c2a..f56966edbf5c62 100644 --- a/arch/riscv/include/asm/usercfi.h +++ b/arch/riscv/include/asm/usercfi.h @@ -39,7 +39,7 @@ void set_active_shstk(struct task_struct *task, unsigned long shstk_addr); bool is_shstk_enabled(struct task_struct *task); bool is_shstk_locked(struct task_struct *task); bool is_shstk_allocated(struct task_struct *task); -void set_shstk_lock(struct task_struct *task); +void set_shstk_lock(struct task_struct *task, bool lock); void set_shstk_status(struct task_struct *task, bool enable); unsigned long get_active_shstk(struct task_struct *task); int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr); @@ -47,7 +47,7 @@ int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr); bool is_indir_lp_enabled(struct task_struct *task); bool is_indir_lp_locked(struct task_struct *task); void set_indir_lp_status(struct task_struct *task, bool enable); -void set_indir_lp_lock(struct task_struct *task); +void set_indir_lp_lock(struct task_struct *task, bool lock); #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE) @@ -69,7 +69,7 @@ void set_indir_lp_lock(struct task_struct *task); #define is_shstk_allocated(task) false -#define set_shstk_lock(task) do {} while (0) +#define set_shstk_lock(task, lock) do {} while (0) #define set_shstk_status(task, enable) do {} while (0) @@ -79,7 +79,7 @@ void set_indir_lp_lock(struct task_struct *task); #define set_indir_lp_status(task, enable) do {} while (0) -#define set_indir_lp_lock(task) do {} while (0) +#define set_indir_lp_lock(task, lock) do {} while (0) #define restore_user_shstk(tsk, shstk_ptr) -EINVAL diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 5957effab57c1d..b2df7f72241a5f 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -160,6 +160,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, * clear shadow stack state on exec. * libc will set it later via prctl. */ + set_shstk_lock(current, false); set_shstk_status(current, false); set_shstk_base(current, 0, 0); set_active_shstk(current, 0); @@ -167,6 +168,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, * disable indirect branch tracking on exec. * libc will enable it later via prctl. */ + set_indir_lp_lock(current, false); set_indir_lp_status(current, false); #ifdef CONFIG_64BIT diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index 1adba746f164e5..9052171c1a8cc4 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -74,9 +74,9 @@ void set_shstk_status(struct task_struct *task, bool enable) csr_write(CSR_ENVCFG, task->thread.envcfg); } -void set_shstk_lock(struct task_struct *task) +void set_shstk_lock(struct task_struct *task, bool lock) { - task->thread_info.user_cfi_state.ubcfi_locked = 1; + task->thread_info.user_cfi_state.ubcfi_locked = lock; } bool is_indir_lp_enabled(struct task_struct *task) @@ -104,9 +104,9 @@ void set_indir_lp_status(struct task_struct *task, bool enable) csr_write(CSR_ENVCFG, task->thread.envcfg); } -void set_indir_lp_lock(struct task_struct *task) +void set_indir_lp_lock(struct task_struct *task, bool lock) { - task->thread_info.user_cfi_state.ufcfi_locked = 1; + task->thread_info.user_cfi_state.ufcfi_locked = lock; } /* * If size is 0, then to be compatible with regular stack we want it to be as big as @@ -452,7 +452,7 @@ int arch_lock_shadow_stack_status(struct task_struct *task, !is_shstk_enabled(task) || arg != 0) return -EINVAL; - set_shstk_lock(task); + set_shstk_lock(task, true); return 0; } @@ -502,7 +502,7 @@ int arch_lock_indir_br_lp_status(struct task_struct *task, !is_indir_lp_enabled(task) || arg != 0) return -EINVAL; - set_indir_lp_lock(task); + set_indir_lp_lock(task, true); return 0; } From ac4e61c730d778f8d8b8455da052952dbf8e0317 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 4 Apr 2026 18:40:58 -0600 Subject: [PATCH 472/700] riscv: ptrace: expand "LP" references to "branch landing pads" in uapi headers Per Linus' comments about the unreadability of abbreviations such as "LP", rename the RISC-V ptrace landing pad CFI macro names to be more explicit. This primarily involves expanding "LP" in the names to some variant of "branch landing pad." Link: https://lore.kernel.org/linux-riscv/CAHk-=whhSLGZAx3N5jJpb4GLFDqH_QvS07D+6BnkPWmCEzTAgw@mail.gmail.com/ Cc: Deepak Gupta Signed-off-by: Paul Walmsley --- arch/riscv/include/uapi/asm/ptrace.h | 20 +++++++++++--------- arch/riscv/kernel/ptrace.c | 10 +++++----- tools/testing/selftests/riscv/cfi/cfitests.c | 5 +++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 69d71443e111e8..9985bd58148ee2 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -132,23 +132,25 @@ struct __sc_riscv_cfi_state { unsigned long ss_ptr; /* shadow stack pointer */ }; -#define PTRACE_CFI_LP_EN_BIT 0 -#define PTRACE_CFI_LP_LOCK_BIT 1 -#define PTRACE_CFI_ELP_BIT 2 +#define PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT 0 +#define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT 1 +#define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT 2 #define PTRACE_CFI_SS_EN_BIT 3 #define PTRACE_CFI_SS_LOCK_BIT 4 #define PTRACE_CFI_SS_PTR_BIT 5 -#define PTRACE_CFI_LP_EN_STATE _BITUL(PTRACE_CFI_LP_EN_BIT) -#define PTRACE_CFI_LP_LOCK_STATE _BITUL(PTRACE_CFI_LP_LOCK_BIT) -#define PTRACE_CFI_ELP_STATE _BITUL(PTRACE_CFI_ELP_BIT) +#define PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT) +#define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE \ + _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT) +#define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE \ + _BITUL(PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT) #define PTRACE_CFI_SS_EN_STATE _BITUL(PTRACE_CFI_SS_EN_BIT) #define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) #define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) -#define PTRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ - PTRACE_CFI_LP_LOCK_STATE | \ - PTRACE_CFI_ELP_STATE | \ +#define PTRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \ + PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | \ + PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE | \ PTRACE_CFI_SS_EN_STATE | \ PTRACE_CFI_SS_LOCK_STATE | \ PTRACE_CFI_SS_PTR_STATE) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 2704a532e91645..b6156625d6f80f 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -303,11 +303,11 @@ static int riscv_cfi_get(struct task_struct *target, regs = task_pt_regs(target); if (is_indir_lp_enabled(target)) { - user_cfi.cfi_status.cfi_state |= PTRACE_CFI_LP_EN_STATE; + user_cfi.cfi_status.cfi_state |= PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE; user_cfi.cfi_status.cfi_state |= is_indir_lp_locked(target) ? - PTRACE_CFI_LP_LOCK_STATE : 0; + PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE : 0; user_cfi.cfi_status.cfi_state |= (regs->status & SR_ELP) ? - PTRACE_CFI_ELP_STATE : 0; + PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE : 0; } if (is_shstk_enabled(target)) { @@ -349,7 +349,7 @@ static int riscv_cfi_set(struct task_struct *target, * rsvd field should be set to zero so that if those fields are needed in future */ if ((user_cfi.cfi_status.cfi_state & - (PTRACE_CFI_LP_EN_STATE | PTRACE_CFI_LP_LOCK_STATE | + (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) || (user_cfi.cfi_status.cfi_state & PTRACE_CFI_STATE_INVALID_MASK)) return -EINVAL; @@ -357,7 +357,7 @@ static int riscv_cfi_set(struct task_struct *target, /* If lpad is enabled on target and ptrace requests to set / clear elp, do that */ if (is_indir_lp_enabled(target)) { if (user_cfi.cfi_status.cfi_state & - PTRACE_CFI_ELP_STATE) /* set elp state */ + PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE) /* set elp state */ regs->status |= SR_ELP; else regs->status &= ~SR_ELP; /* clear elp state */ diff --git a/tools/testing/selftests/riscv/cfi/cfitests.c b/tools/testing/selftests/riscv/cfi/cfitests.c index 29854485441577..68374a27ec81b0 100644 --- a/tools/testing/selftests/riscv/cfi/cfitests.c +++ b/tools/testing/selftests/riscv/cfi/cfitests.c @@ -94,7 +94,7 @@ bool cfi_ptrace_test(void) } switch (ptrace_test_num) { -#define CFI_ENABLE_MASK (PTRACE_CFI_LP_EN_STATE | \ +#define CFI_ENABLE_MASK (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \ PTRACE_CFI_SS_EN_STATE | \ PTRACE_CFI_SS_PTR_STATE) case 0: @@ -106,7 +106,8 @@ bool cfi_ptrace_test(void) __func__); break; case 1: - if (!(cfi_reg.cfi_status.cfi_state & PTRACE_CFI_ELP_STATE)) + if (!(cfi_reg.cfi_status.cfi_state & + PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE)) ksft_exit_fail_msg("%s: elp must have been set\n", __func__); /* clear elp state. not interested in anything else */ cfi_reg.cfi_status.cfi_state = 0; From adfc80dd0d7831335b5105fb3d8747094bf42878 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 4 Apr 2026 18:40:58 -0600 Subject: [PATCH 473/700] prctl: rename branch landing pad implementation functions to be more explicit Per Linus' comments about the unreadability of abbreviations such as "indir_br_lp", rename the three prctl() implementation functions to be more explicit. This involves renaming "indir_br_lp_status" in the function names to "branch_landing_pad_state". While here, add _prctl_ into the function names, following the speculation control prctl implementation functions. Link: https://lore.kernel.org/linux-riscv/CAHk-=whhSLGZAx3N5jJpb4GLFDqH_QvS07D+6BnkPWmCEzTAgw@mail.gmail.com/ Cc: Deepak Gupta Cc: Linus Torvalds Cc: Mark Brown Signed-off-by: Paul Walmsley --- arch/riscv/kernel/usercfi.c | 16 ++++++++-------- include/linux/cpu.h | 6 +++--- kernel/sys.c | 15 ++++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index 9052171c1a8cc4..04ab1eb8df297b 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -457,7 +457,8 @@ int arch_lock_shadow_stack_status(struct task_struct *task, return 0; } -int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) +int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, + unsigned long __user *state) { unsigned long fcfi_status = 0; @@ -467,10 +468,10 @@ int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *sta /* indirect branch tracking is enabled on the task or not */ fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0); - return copy_to_user(status, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; + return copy_to_user(state, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; } -int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) +int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state) { bool enable_indir_lp = false; @@ -482,24 +483,23 @@ int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) return -EINVAL; /* Reject unknown flags */ - if (status & ~PR_INDIR_BR_LP_ENABLE) + if (state & ~PR_INDIR_BR_LP_ENABLE) return -EINVAL; - enable_indir_lp = (status & PR_INDIR_BR_LP_ENABLE); + enable_indir_lp = (state & PR_INDIR_BR_LP_ENABLE); set_indir_lp_status(t, enable_indir_lp); return 0; } -int arch_lock_indir_br_lp_status(struct task_struct *task, - unsigned long arg) +int arch_prctl_lock_branch_landing_pad_state(struct task_struct *task) { /* * If indirect branch tracking is not supported or not enabled on task, * nothing to lock here */ if (!is_user_lpad_enabled() || - !is_indir_lp_enabled(task) || arg != 0) + !is_indir_lp_enabled(task)) return -EINVAL; set_indir_lp_lock(task, true); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 8239cd95a00533..9b6b0d87fdb056 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -229,8 +229,8 @@ static inline bool cpu_attack_vector_mitigated(enum cpu_attack_vectors v) #define smt_mitigations SMT_MITIGATIONS_OFF #endif -int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status); -int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status); -int arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status); +int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, unsigned long __user *state); +int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state); +int arch_prctl_lock_branch_landing_pad_state(struct task_struct *t); #endif /* _LINUX_CPU_H_ */ diff --git a/kernel/sys.c b/kernel/sys.c index c86eba9aa7e9df..a5e0c187bbbf3d 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2388,17 +2388,18 @@ int __weak arch_lock_shadow_stack_status(struct task_struct *t, unsigned long st return -EINVAL; } -int __weak arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) +int __weak arch_prctl_get_branch_landing_pad_state(struct task_struct *t, + unsigned long __user *state) { return -EINVAL; } -int __weak arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) +int __weak arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state) { return -EINVAL; } -int __weak arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status) +int __weak arch_prctl_lock_branch_landing_pad_state(struct task_struct *t) { return -EINVAL; } @@ -2891,17 +2892,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_GET_INDIR_BR_LP_STATUS: if (arg3 || arg4 || arg5) return -EINVAL; - error = arch_get_indir_br_lp_status(me, (unsigned long __user *)arg2); + error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg2); break; case PR_SET_INDIR_BR_LP_STATUS: if (arg3 || arg4 || arg5) return -EINVAL; - error = arch_set_indir_br_lp_status(me, arg2); + error = arch_prctl_set_branch_landing_pad_state(me, arg2); break; case PR_LOCK_INDIR_BR_LP_STATUS: - if (arg3 || arg4 || arg5) + if (arg2 || arg3 || arg4 || arg5) return -EINVAL; - error = arch_lock_indir_br_lp_status(me, arg2); + error = arch_prctl_lock_branch_landing_pad_state(me); break; default: trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5); From e5342fe2c1bb5b4fab6ed531a0122c6417e57ecf Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 4 Apr 2026 18:40:58 -0600 Subject: [PATCH 474/700] riscv: ptrace: cfi: expand "SS" references to "shadow stack" in uapi headers Similar to the recent change to expand "LP" to "branch landing pad", let's expand "SS" in the ptrace uapi macros to "shadow stack" as well. This aligns with the existing prctl() arguments, which use the expanded "shadow stack" names, rather than just the abbreviation. Link: https://lore.kernel.org/linux-riscv/CAHk-=whhSLGZAx3N5jJpb4GLFDqH_QvS07D+6BnkPWmCEzTAgw@mail.gmail.com/ Cc: Deepak Gupta Signed-off-by: Paul Walmsley --- arch/riscv/include/uapi/asm/ptrace.h | 18 +++++++++--------- arch/riscv/kernel/ptrace.c | 10 +++++----- tools/testing/selftests/riscv/cfi/cfitests.c | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 9985bd58148ee2..3de2b7124aff3e 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -135,25 +135,25 @@ struct __sc_riscv_cfi_state { #define PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT 0 #define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT 1 #define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT 2 -#define PTRACE_CFI_SS_EN_BIT 3 -#define PTRACE_CFI_SS_LOCK_BIT 4 -#define PTRACE_CFI_SS_PTR_BIT 5 +#define PTRACE_CFI_SHADOW_STACK_EN_BIT 3 +#define PTRACE_CFI_SHADOW_STACK_LOCK_BIT 4 +#define PTRACE_CFI_SHADOW_STACK_PTR_BIT 5 #define PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT) #define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE \ _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT) #define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE \ _BITUL(PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT) -#define PTRACE_CFI_SS_EN_STATE _BITUL(PTRACE_CFI_SS_EN_BIT) -#define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) -#define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) +#define PTRACE_CFI_SHADOW_STACK_EN_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_EN_BIT) +#define PTRACE_CFI_SHADOW_STACK_LOCK_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_LOCK_BIT) +#define PTRACE_CFI_SHADOW_STACK_PTR_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_PTR_BIT) #define PTRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \ PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | \ PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE | \ - PTRACE_CFI_SS_EN_STATE | \ - PTRACE_CFI_SS_LOCK_STATE | \ - PTRACE_CFI_SS_PTR_STATE) + PTRACE_CFI_SHADOW_STACK_EN_STATE | \ + PTRACE_CFI_SHADOW_STACK_LOCK_STATE | \ + PTRACE_CFI_SHADOW_STACK_PTR_STATE) struct __cfi_status { __u64 cfi_state; diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index b6156625d6f80f..93de2e7a30747d 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -311,10 +311,10 @@ static int riscv_cfi_get(struct task_struct *target, } if (is_shstk_enabled(target)) { - user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SS_EN_STATE | - PTRACE_CFI_SS_PTR_STATE); + user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SHADOW_STACK_EN_STATE | + PTRACE_CFI_SHADOW_STACK_PTR_STATE); user_cfi.cfi_status.cfi_state |= is_shstk_locked(target) ? - PTRACE_CFI_SS_LOCK_STATE : 0; + PTRACE_CFI_SHADOW_STACK_LOCK_STATE : 0; user_cfi.shstk_ptr = get_active_shstk(target); } @@ -350,7 +350,7 @@ static int riscv_cfi_set(struct task_struct *target, */ if ((user_cfi.cfi_status.cfi_state & (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | - PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) || + PTRACE_CFI_SHADOW_STACK_EN_STATE | PTRACE_CFI_SHADOW_STACK_LOCK_STATE)) || (user_cfi.cfi_status.cfi_state & PTRACE_CFI_STATE_INVALID_MASK)) return -EINVAL; @@ -365,7 +365,7 @@ static int riscv_cfi_set(struct task_struct *target, /* If shadow stack enabled on target, set new shadow stack pointer */ if (is_shstk_enabled(target) && - (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SS_PTR_STATE)) + (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SHADOW_STACK_PTR_STATE)) set_active_shstk(target, user_cfi.shstk_ptr); return 0; diff --git a/tools/testing/selftests/riscv/cfi/cfitests.c b/tools/testing/selftests/riscv/cfi/cfitests.c index 68374a27ec81b0..0dac74b8553cdb 100644 --- a/tools/testing/selftests/riscv/cfi/cfitests.c +++ b/tools/testing/selftests/riscv/cfi/cfitests.c @@ -95,8 +95,8 @@ bool cfi_ptrace_test(void) switch (ptrace_test_num) { #define CFI_ENABLE_MASK (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \ - PTRACE_CFI_SS_EN_STATE | \ - PTRACE_CFI_SS_PTR_STATE) + PTRACE_CFI_SHADOW_STACK_EN_STATE | \ + PTRACE_CFI_SHADOW_STACK_PTR_STATE) case 0: if ((cfi_reg.cfi_status.cfi_state & CFI_ENABLE_MASK) != CFI_ENABLE_MASK) ksft_exit_fail_msg("%s: ptrace_getregset failed, %llu\n", __func__, From 08ee1559052be302f1d3752f48360b89517d9f8d Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 4 Apr 2026 18:40:58 -0600 Subject: [PATCH 475/700] prctl: cfi: change the branch landing pad prctl()s to be more descriptive Per Linus' comments requesting the replacement of "INDIR_BR_LP" in the indirect branch tracking prctl()s with something more readable, and suggesting the use of the speculation control prctl()s as an exemplar, reimplement the prctl()s and related constants that control per-task forward-edge control flow integrity. This primarily involves two changes. First, the prctls are restructured to resemble the style of the speculative execution workaround control prctls PR_{GET,SET}_SPECULATION_CTRL, to make them easier to extend in the future. Second, the "indir_br_lp" abbrevation is expanded to "branch_landing_pads" to be less telegraphic. The kselftest and documentation is adjusted accordingly. Link: https://lore.kernel.org/linux-riscv/CAHk-=whhSLGZAx3N5jJpb4GLFDqH_QvS07D+6BnkPWmCEzTAgw@mail.gmail.com/ Cc: Deepak Gupta Cc: Linus Torvalds Cc: Mark Brown Signed-off-by: Paul Walmsley --- Documentation/arch/riscv/zicfilp.rst | 63 ++++++++++++------- arch/riscv/kernel/usercfi.c | 15 +++-- include/uapi/linux/prctl.h | 37 +++++------ kernel/sys.c | 23 ++++--- .../trace/beauty/include/uapi/linux/prctl.h | 36 +++++------ tools/testing/selftests/riscv/cfi/cfitests.c | 4 +- 6 files changed, 91 insertions(+), 87 deletions(-) diff --git a/Documentation/arch/riscv/zicfilp.rst b/Documentation/arch/riscv/zicfilp.rst index 78a3e01ff68c0a..ab7d8e62ddaffb 100644 --- a/Documentation/arch/riscv/zicfilp.rst +++ b/Documentation/arch/riscv/zicfilp.rst @@ -76,34 +76,49 @@ the program. 4. prctl() enabling -------------------- -:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` / -:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect -branch tracking. These prctls are architecture-agnostic and return -EINVAL if -the underlying functionality is not supported. +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 +: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. -* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg) +* prctl(:c:macro:`PR_SET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long arg) -If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports -``zicfilp`` then the kernel will enable indirect branch tracking for the -task. The dynamic loader can issue this :c:macro:`prctl` once it has -determined that all the objects loaded in the address space support -indirect branch tracking. Additionally, if there is a `dlopen` to an -object which wasn't compiled with ``zicfilp``, the dynamic loader can -issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE` -cleared). - -* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg) +arg is a bitmask. -Returns the current status of indirect branch tracking. If enabled -it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE` - -* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg) +If :c:macro:`PR_CFI_ENABLE` is set in arg, and the CPU supports +``zicfilp``, then the kernel will enable indirect branch tracking for +the task. The dynamic loader can issue this ``prctl()`` once it has +determined that all the objects loaded in the address space support +indirect branch tracking. + +Indirect branch tracking state can also be locked once enabled. This +prevents the task from subsequently disabling it. This is done by +setting the bit :c:macro:`PR_CFI_LOCK` in arg. Either indirect branch +tracking must already be enabled for the task, or the bit +:c:macro:`PR_CFI_ENABLE` must also be set in arg. This is intended +for environments that wish to run with a strict security posture that +do not wish to load objects without ``zicfilp`` support. + +Indirect branch tracking can also be disabled for the task, assuming +that it has not previously been enabled and locked. If there is a +``dlopen()`` to an object which wasn't compiled with ``zicfilp``, the +dynamic loader can issue this ``prctl()`` with arg set to +:c:macro:`PR_CFI_DISABLE`. Disabling indirect branch tracking for the +task is not possible if it has previously been enabled and locked. + + +* prctl(:c:macro:`PR_GET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long * arg) + +Returns the current status of indirect branch tracking into a bitmask +stored into the memory location pointed to by arg. The bitmask will +have the :c:macro:`PR_CFI_ENABLE` bit set if indirect branch tracking +is currently enabled for the task, and if it is locked, will +additionally have the :c:macro:`PR_CFI_LOCK` bit set. If indirect +branch tracking is currently disabled for the task, the +:c:macro:`PR_CFI_DISABLE` bit will be set. -Locks the current status of indirect branch tracking on the task. User -space may want to run with a strict security posture and wouldn't want -loading of objects without ``zicfilp`` support in them, to disallow -disabling of indirect branch tracking. In this case, user space can -use this prctl to lock the current settings. 5. violations related to indirect branch tracking -------------------------------------------------- diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index 04ab1eb8df297b..2c535737511dce 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -465,16 +465,14 @@ int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, if (!is_user_lpad_enabled()) return -EINVAL; - /* indirect branch tracking is enabled on the task or not */ - fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0); + fcfi_status = (is_indir_lp_enabled(t) ? PR_CFI_ENABLE : PR_CFI_DISABLE); + fcfi_status |= (is_indir_lp_locked(t) ? PR_CFI_LOCK : 0); return copy_to_user(state, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; } int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state) { - bool enable_indir_lp = false; - if (!is_user_lpad_enabled()) return -EINVAL; @@ -482,12 +480,13 @@ int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long if (is_indir_lp_locked(t)) return -EINVAL; - /* Reject unknown flags */ - if (state & ~PR_INDIR_BR_LP_ENABLE) + if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE))) + return -EINVAL; + + if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE) return -EINVAL; - enable_indir_lp = (state & PR_INDIR_BR_LP_ENABLE); - set_indir_lp_status(t, enable_indir_lp); + set_indir_lp_status(t, !!(state & PR_CFI_ENABLE)); return 0; } diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 55b0446fff9d95..b6ec6f6937195f 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -397,30 +397,23 @@ struct prctl_mm_map { # define PR_RSEQ_SLICE_EXT_ENABLE 0x01 /* - * Get the current indirect branch tracking configuration for the current - * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. + * Get or set the control flow integrity (CFI) configuration for the + * current thread. + * + * Some per-thread control flow integrity settings are not yet + * controlled through this prctl(); see for example + * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS */ -#define PR_GET_INDIR_BR_LP_STATUS 80 - +#define PR_GET_CFI 80 +#define PR_SET_CFI 81 /* - * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will - * enable cpu feature for user thread, to track all indirect branches and ensure - * they land on arch defined landing pad instruction. - * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction. - * arch64 - If enabled, an indirect branch must land on a BTI instruction. - * riscv - If enabled, an indirect branch must land on an lpad instruction. - * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect - * branches will no more be tracked by cpu to land on arch defined landing pad - * instruction. - */ -#define PR_SET_INDIR_BR_LP_STATUS 81 -# define PR_INDIR_BR_LP_ENABLE (1UL << 0) - -/* - * Prevent further changes to the specified indirect branch tracking - * configuration. All bits may be locked via this call, including - * undefined bits. + * Forward-edge CFI variants (excluding ARM64 BTI, which has its own + * prctl()s). */ -#define PR_LOCK_INDIR_BR_LP_STATUS 82 +#define PR_CFI_BRANCH_LANDING_PADS 0 +/* Return and control values for PR_{GET,SET}_CFI */ +# define PR_CFI_ENABLE _BITUL(0) +# define PR_CFI_DISABLE _BITUL(1) +# define PR_CFI_LOCK _BITUL(2) #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index a5e0c187bbbf3d..62e842055cc9c7 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2889,20 +2889,23 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return -EINVAL; error = rseq_slice_extension_prctl(arg2, arg3); break; - case PR_GET_INDIR_BR_LP_STATUS: - if (arg3 || arg4 || arg5) + case PR_GET_CFI: + if (arg2 != PR_CFI_BRANCH_LANDING_PADS) return -EINVAL; - error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg2); - break; - case PR_SET_INDIR_BR_LP_STATUS: - if (arg3 || arg4 || arg5) + if (arg4 || arg5) return -EINVAL; - error = arch_prctl_set_branch_landing_pad_state(me, arg2); + error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg3); break; - case PR_LOCK_INDIR_BR_LP_STATUS: - if (arg2 || arg3 || arg4 || arg5) + case PR_SET_CFI: + if (arg2 != PR_CFI_BRANCH_LANDING_PADS) return -EINVAL; - error = arch_prctl_lock_branch_landing_pad_state(me); + if (arg4 || arg5) + return -EINVAL; + error = arch_prctl_set_branch_landing_pad_state(me, arg3); + if (error) + break; + if (arg3 & PR_CFI_LOCK && !(arg3 & PR_CFI_DISABLE)) + error = arch_prctl_lock_branch_landing_pad_state(me); break; default: trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5); diff --git a/tools/perf/trace/beauty/include/uapi/linux/prctl.h b/tools/perf/trace/beauty/include/uapi/linux/prctl.h index 55b0446fff9d95..560f99bc4782b3 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/prctl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/prctl.h @@ -397,30 +397,24 @@ struct prctl_mm_map { # define PR_RSEQ_SLICE_EXT_ENABLE 0x01 /* - * Get the current indirect branch tracking configuration for the current - * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. + * Get or set the control flow integrity (CFI) configuration for the + * current thread. + * + * Some per-thread control flow integrity settings are not yet + * controlled through this prctl(); see for example + * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS */ -#define PR_GET_INDIR_BR_LP_STATUS 80 - +#define PR_GET_CFI 80 +#define PR_SET_CFI 81 /* - * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will - * enable cpu feature for user thread, to track all indirect branches and ensure - * they land on arch defined landing pad instruction. - * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction. - * arch64 - If enabled, an indirect branch must land on a BTI instruction. - * riscv - If enabled, an indirect branch must land on an lpad instruction. - * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect - * branches will no more be tracked by cpu to land on arch defined landing pad - * instruction. + * Forward-edge CFI variants (excluding ARM64 BTI, which has its own + * prctl()s). */ -#define PR_SET_INDIR_BR_LP_STATUS 81 -# define PR_INDIR_BR_LP_ENABLE (1UL << 0) +#define PR_CFI_BRANCH_LANDING_PADS 0 +/* Return and control values for PR_{GET,SET}_CFI */ +# define PR_CFI_ENABLE _BITUL(0) +# define PR_CFI_DISABLE _BITUL(1) +# define PR_CFI_LOCK _BITUL(2) -/* - * Prevent further changes to the specified indirect branch tracking - * configuration. All bits may be locked via this call, including - * undefined bits. - */ -#define PR_LOCK_INDIR_BR_LP_STATUS 82 #endif /* _LINUX_PRCTL_H */ diff --git a/tools/testing/selftests/riscv/cfi/cfitests.c b/tools/testing/selftests/riscv/cfi/cfitests.c index 0dac74b8553cdb..39d097b6881ff2 100644 --- a/tools/testing/selftests/riscv/cfi/cfitests.c +++ b/tools/testing/selftests/riscv/cfi/cfitests.c @@ -146,11 +146,11 @@ int main(int argc, char *argv[]) * pads for user mode except lighting up a bit in senvcfg via a prctl. * Enable landing pad support throughout the execution of the test binary. */ - ret = my_syscall5(__NR_prctl, PR_GET_INDIR_BR_LP_STATUS, &lpad_status, 0, 0, 0); + ret = my_syscall5(__NR_prctl, PR_GET_CFI, PR_CFI_BRANCH_LANDING_PADS, &lpad_status, 0, 0); if (ret) ksft_exit_fail_msg("Get landing pad status failed with %d\n", ret); - if (!(lpad_status & PR_INDIR_BR_LP_ENABLE)) + if (!(lpad_status & PR_CFI_ENABLE)) ksft_exit_fail_msg("Landing pad is not enabled, should be enabled via glibc\n"); ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &ss_status, 0, 0, 0); From 0422b07bc4c296b736e240d95d21fbfebbfaa2ca Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Sat, 28 Feb 2026 09:08:14 -0500 Subject: [PATCH 476/700] x86/mce/amd: Filter bogus hardware errors on Zen3 clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users have been observing multiple L3 cache deferred errors after recent kernel rework of deferred error handling.¹ ⁴ The errors are bogus due to inconsistent status values. Also, user verified that bogus MCA_DESTAT values are present on the system even with an older kernel.² The errors seem to be garbage values present in the MCA_DESTAT of some L3 cache banks. These were implicitly ignored before the recent kernel rework because these do not generate a deferred error interrupt. A later revision of the rework patch was merged for v6.19. This naturally filtered out most of the bogus error logs. However, a few signatures still remain.³ Minimize the scope of the filter to the reported CPU family/model/stepping and only for errors which don't have the Enabled bit in the MCi status MSR. ¹ https://lore.kernel.org/20250915010010.3547-1-spasswolf@web.de ² https://lore.kernel.org/6e1eda7dd55f6fa30405edf7b0f75695cf55b237.camel@web.de ³ https://lore.kernel.org/21ba47fa8893b33b94370c2a42e5084cf0d2e975.camel@web.de ⁴ https://lore.kernel.org/r/CAKFB093B2k3sKsGJ_QNX1jVQsaXVFyy=wNwpzCGLOXa_vSDwXw@mail.gmail.com [ bp: Generalize the condition according to which errors are bogus. ] Fixes: 7cb735d7c0cb ("x86/mce: Unify AMD DFR handler with MCA Polling") Closes: https://lore.kernel.org/20250915010010.3547-1-spasswolf@web.de Reported-by: Bert Karwatzki Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Mario Limonciello Tested-By: Bert Karwatzki Cc: stable@vger.kernel.org Link: https://lore.kernel.org/20250915010010.3547-1-spasswolf@web.de --- arch/x86/kernel/cpu/mce/amd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index a030ee4cecc2fe..28deaba088339e 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -604,6 +604,14 @@ bool amd_filter_mce(struct mce *m) enum smca_bank_types bank_type = smca_get_bank_type(m->extcpu, m->bank); struct cpuinfo_x86 *c = &boot_cpu_data; + /* Bogus hw errors on Cezanne A0. */ + if (c->x86 == 0x19 && + c->x86_model == 0x50 && + c->x86_stepping == 0x0) { + if (!(m->status & MCI_STATUS_EN)) + return true; + } + /* See Family 17h Models 10h-2Fh Erratum #1114. */ if (c->x86 == 0x17 && c->x86_model >= 0x10 && c->x86_model <= 0x2F && From 51520e03e70d6c73e33ee7cbe0319767d05764fe Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Tue, 31 Mar 2026 14:16:23 +0200 Subject: [PATCH 477/700] EDAC/mc: Fix error path ordering in edac_mc_alloc() When the mci->pvt_info allocation in edac_mc_alloc() fails, the error path will call put_device() which will end up calling the device's release function. However, the init ordering is wrong such that device_initialize() happens *after* the failed allocation and thus the device itself and the release function pointer are not initialized yet when they're called: MCE: In-kernel MCE decoding enabled. ------------[ cut here ]------------ kobject: '(null)': is not initialized, yet kobject_put() is being called. WARNING: lib/kobject.c:734 at kobject_put, CPU#22: systemd-udevd CPU: 22 UID: 0 PID: 538 Comm: systemd-udevd Not tainted 7.0.0-rc1+ #2 PREEMPT(full) RIP: 0010:kobject_put Call Trace: edac_mc_alloc+0xbe/0xe0 [edac_core] amd64_edac_init+0x7a4/0xff0 [amd64_edac] ? __pfx_amd64_edac_init+0x10/0x10 [amd64_edac] do_one_initcall ... Reorder the calling sequence so that the device is initialized and thus the release function pointer is properly set before it can be used. This was found by Claude while reviewing another EDAC patch. Fixes: 0bbb265f7089 ("EDAC/mc: Get rid of silly one-shot struct allocation in edac_mc_alloc()") Reported-by: Claude Code:claude-opus-4.5 Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Qiuxu Zhuo Cc: stable@kernel.org Link: https://patch.msgid.link/20260331121623.4871-1-bp@kernel.org --- drivers/edac/edac_mc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 29e9828422bb07..11d66333d93b43 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -369,13 +369,13 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, if (!mci->layers) goto error; + mci->dev.release = mci_release; + device_initialize(&mci->dev); + mci->pvt_info = kzalloc(sz_pvt, GFP_KERNEL); if (!mci->pvt_info) goto error; - mci->dev.release = mci_release; - device_initialize(&mci->dev); - /* setup index and various internal pointers */ mci->mc_idx = mc_num; mci->tot_dimms = tot_dimms; From 5401b9adebc9e5f68df58226f51493ef0e6ceb4d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Apr 2026 12:42:25 -0700 Subject: [PATCH 478/700] i915: don't use a vma that didn't match the context VM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In eb_lookup_vma(), the code checks that the context vm matches before incrementing the i915 vma usage count, but for the non-matching case it didn't clear the non-matching vma pointer, so it would then mistakenly be returned, causing potential UaF and refcount issues. Reported-by: Yassine Mounir Suggested-by: Ville Syrjälä Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index e7918f896a26fe..942f4eed817f3c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -898,6 +898,8 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle) vma = radix_tree_lookup(&eb->gem_context->handles_vma, handle); if (likely(vma && vma->vm == vm)) vma = i915_vma_tryget(vma); + else + vma = NULL; rcu_read_unlock(); if (likely(vma)) return vma; From 591cd656a1bf5ea94a222af5ef2ee76df029c1d2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 5 Apr 2026 15:26:23 -0700 Subject: [PATCH 479/700] Linux 7.0-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6b1d9fb1a6b496..4f54c568563864 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 0 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Baby Opossum Posse # *DOCUMENTATION* From 4346be6577aaa04586167402ae87bbdbe32484a4 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 00:03:15 +0800 Subject: [PATCH 480/700] tracing/probe: reject non-closed empty immediate strings parse_probe_arg() accepts quoted immediate strings and passes the body after the opening quote to __parse_imm_string(). That helper currently computes strlen(str) and immediately dereferences str[len - 1], which underflows when the body is empty and not closed with double-quotation. Reject empty non-closed immediate strings before checking for the closing quote. Link: https://lore.kernel.org/all/20260401160315.88518-1-pengpeng@iscas.ac.cn/ Fixes: a42e3c4de964 ("tracing/probe: Add immediate string parameter support") Signed-off-by: Pengpeng Hou Reviewed-by: Steven Rostedt (Google) Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index e0a5dc86c07e97..e1c73065dae51b 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1068,7 +1068,7 @@ static int __parse_imm_string(char *str, char **pbuf, int offs) { size_t len = strlen(str); - if (str[len - 1] != '"') { + if (!len || str[len - 1] != '"') { trace_probe_log_err(offs + len, IMMSTR_NO_CLOSE); return -EINVAL; } From 9551af27f8167bbb5f862a12f7f9bc5830e8f4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 3 Apr 2026 00:47:13 -0300 Subject: [PATCH 481/700] ALSA: aoa: onyx: Update IEC958 sample-rate status for PCM playback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onyx_prepare() accepts 32/44.1/48 kHz PCM playback, but it leaves the Onyx IEC958 sample-rate status bits at the driver's initial 44.1 kHz setting in DIG_INFO3. As a result, 32 kHz and 48 kHz PCM streams advertise a stale IEC958 sample rate unless userspace rewrites IEC958 Playback Default first. Update only the consumer sample-frequency bits in DIG_INFO3 from the PCM runtime during prepare, resolving the long-standing FIXME in the PCM playback path while leaving the other user-controlled IEC958 status bits unchanged. Mark IEC958 Playback Default as volatile as well, since prepare() now changes the exposed register contents outside the control put callback. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260403-onyx-spdif-pcm-rate-v1-1-dcfaf931cf83@gmail.com Signed-off-by: Takashi Iwai --- sound/aoa/codecs/onyx.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index da0eebf5dfbc2d..4fb593e88fb18d 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -32,6 +32,7 @@ #include #include #include +#include MODULE_AUTHOR("Johannes Berg "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); @@ -514,8 +515,36 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol, return 1; } +static int onyx_set_spdif_pcm_rate(struct onyx *onyx, unsigned int rate) +{ + u8 dig_info3, fs; + + switch (rate) { + case 32000: + fs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + fs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + fs = IEC958_AES3_CON_FS_48000; + break; + default: + return -EINVAL; + } + + if (onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &dig_info3)) + return -EBUSY; + dig_info3 = (dig_info3 & ~IEC958_AES3_CON_FS) | fs; + if (onyx_write_register(onyx, ONYX_REG_DIG_INFO3, dig_info3)) + return -EBUSY; + + return 0; +} + static const struct snd_kcontrol_new onyx_spdif_ctrl = { - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = onyx_spdif_info, @@ -695,9 +724,9 @@ static int onyx_prepare(struct codec_info_item *cii, case 32000: case 44100: case 48000: - /* these rates are ok for all outputs */ - /* FIXME: program spdif channel control bits here so that - * userspace doesn't have to if it only plays pcm! */ + if (onyx->codec.connected & 2) + return onyx_set_spdif_pcm_rate(onyx, + substream->runtime->rate); return 0; default: /* got some rate that the digital output can't do, From 7b5b7d04498d84bc7abc05b4e09a0d17c820d3b0 Mon Sep 17 00:00:00 2001 From: songxiebing Date: Sun, 5 Apr 2026 09:42:08 +0800 Subject: [PATCH 482/700] ALSA: hda/realtek: Fix code style error Output of checkpatch shows error: ERROR: else should follow close brace '}' 2168: FILE: sound/hda/codecs/realtek/realtek.c:2168: + } + else So fix it. Signed-off-by: songxiebing Link: https://patch.msgid.link/20260405014208.167364-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/realtek.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index aad265c0a5b611..b240f13b043872 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -2164,8 +2164,7 @@ void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { struct alc_spec *spec = codec->spec; spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - } - else + } else alc_fixup_headset_mode(codec, fix, action); } EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK"); From 2428cd6e8b6fa80c36db4652702ca0acd2ce3f08 Mon Sep 17 00:00:00 2001 From: Panagiotis Petrakopoulos Date: Mon, 6 Apr 2026 01:25:48 +0300 Subject: [PATCH 483/700] ALSA: scarlett2: Add missing sentinel initializer field A "-Wmissing-field-initializers" warning was emitted when compiling the module using the W=2 option. There is a sentinel initializer field missing in the end of scarlett2_devices[]. Tested using a Scarlett Solo 4th gen. Fixes: d98cc489029d ("ALSA: scarlett2: Move USB IDs out from device_info struct") Signed-off-by: Panagiotis Petrakopoulos Link: https://patch.msgid.link/20260405222548.8903-1-npetrakopoulos2003@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index fd1fb668929a25..8eaa962227596f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2262,7 +2262,7 @@ static const struct scarlett2_device_entry scarlett2_devices[] = { { USB_ID(0x1235, 0x820c), &clarett_8pre_info, "Clarett+" }, /* End of list */ - { 0, NULL }, + { 0, NULL, NULL }, }; /* get the starting port index number for a given port type/direction */ From e9418da50d9e5c496c22fe392e4ad74c038a94eb Mon Sep 17 00:00:00 2001 From: Harin Lee Date: Mon, 6 Apr 2026 16:48:57 +0900 Subject: [PATCH 484/700] ALSA: ctxfi: Limit PTP to a single page Commit 391e69143d0a increased CT_PTP_NUM from 1 to 4 to support 256 playback streams, but the additional pages are not used by the card correctly. The CT20K2 hardware already has multiple VMEM_PTPAL registers, but using them separately would require refactoring the entire virtual memory allocation logic. ct_vm_map() always uses PTEs in vm->ptp[0].area regardless of CT_PTP_NUM. On AMD64 systems, a single PTP covers 512 PTEs (2M). When aggregate memory allocations exceed this limit, ct_vm_map() tries to access beyond the allocated space and causes a page fault: BUG: unable to handle page fault for address: ffffd4ae8a10a000 Oops: Oops: 0002 [#1] SMP PTI RIP: 0010:ct_vm_map+0x17c/0x280 [snd_ctxfi] Call Trace: atc_pcm_playback_prepare+0x225/0x3b0 ct_pcm_playback_prepare+0x38/0x60 snd_pcm_do_prepare+0x2f/0x50 snd_pcm_action_single+0x36/0x90 snd_pcm_action_nonatomic+0xbf/0xd0 snd_pcm_ioctl+0x28/0x40 __x64_sys_ioctl+0x97/0xe0 do_syscall_64+0x81/0x610 entry_SYSCALL_64_after_hwframe+0x76/0x7e Revert CT_PTP_NUM to 1. The 256 SRC_RESOURCE_NUM and playback_count remain unchanged. Fixes: 391e69143d0a ("ALSA: ctxfi: Bump playback substreams to 256") Cc: stable@vger.kernel.org Signed-off-by: Harin Lee Link: https://patch.msgid.link/20260406074857.216034-1-me@harin.net Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctvmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index da54cbcdb0be4d..43a0065b40c354 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -15,7 +15,7 @@ #ifndef CTVMEM_H #define CTVMEM_H -#define CT_PTP_NUM 4 /* num of device page table pages */ +#define CT_PTP_NUM 1 /* num of device page table pages */ #include #include From 7d61662197ecdc458e33e475b6ada7f6da61d364 Mon Sep 17 00:00:00 2001 From: Harin Lee Date: Mon, 6 Apr 2026 16:49:13 +0900 Subject: [PATCH 485/700] ALSA: ctxfi: Add fallback to default RSR for S/PDIF spdif_passthru_playback_get_resources() uses atc->pll_rate as the RSR for the MSR calculation loop. However, pll_rate is only updated in atc_pll_init() and not in hw_pll_init(), so it remains 0 after the card init. When spdif_passthru_playback_setup() skips atc_pll_init() for 32000 Hz, (rsr * desc.msr) always becomes 0, causing the loop to spin indefinitely. Add fallback to use atc->rsr when atc->pll_rate is 0. This reflects the hardware state, since hw_card_init() already configures the PLL to the default RSR. Fixes: 8cc72361481f ("ALSA: SB X-Fi driver merge") Cc: stable@vger.kernel.org Signed-off-by: Harin Lee Link: https://patch.msgid.link/20260406074913.217374-1-me@harin.net Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 516c0a12ed9fbe..02fe093309391c 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -794,7 +794,8 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, struct src *src; int err; int n_amixer = apcm->substream->runtime->channels, i; - unsigned int pitch, rsr = atc->pll_rate; + unsigned int pitch; + unsigned int rsr = atc->pll_rate ? atc->pll_rate : atc->rsr; /* first release old resources */ atc_pcm_release_resources(atc, apcm); From f0541edb2e7333f320642c7b491a67912c1f65db Mon Sep 17 00:00:00 2001 From: songxiebing Date: Sun, 5 Apr 2026 09:26:51 +0800 Subject: [PATCH 486/700] ALSA: hda/realtek: Add quirk for Lenovo Yoga Pro 7 14IAH10 The bass speakers are not working, and add the following entry in /etc/modprobe.d/snd.conf: options snd-sof-intel-hda-generic hda_model=alc287-yoga9-bass-spk-pin Fixes the bass speakers. So add the quick ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN here. Reported-by: Fernando Garcia Corona Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221317 Signed-off-by: songxiebing Link: https://patch.msgid.link/20260405012651.133838-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 6db23ce8a5fe98..45f9d648738857 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7680,6 +7680,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), From d38e9457ddf0780dd55c953886ae48abbe4d33b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 6 Apr 2026 00:20:03 -0300 Subject: [PATCH 487/700] ALSA: gus: add shared GF1 suspend and resume helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gusclassic and gusextreme still leave their ISA PM callbacks disabled because the shared GF1 core only provides probe-time startup and full shutdown paths. Those helpers are not suitable for suspend and resume. They reset software handlers and tear down runtime state such as the DRAM allocator, timer state, DMA queues, PCM state and UART setup. Resume instead needs a narrower recovery path that rebuilds the GF1 hardware state without rerunning probe-only detection or discarding the bookkeeping kept by the card instance. Add shared GF1 suspend and resume helpers for that recovery path. Suspend now quiesces GF1 PCM, aborts queued GF1 DMA work, resets the UART and powers the chip down without tearing down allocator, timer or rawmidi bookkeeping. Resume rebuilds the GF1 hardware state, restores timer and UART handlers, and brings the chip back to a usable post-resume state for the ISA front-ends. The scope is limited to restoring post-resume usability. It does not attempt transparent continuation of active GF1 PCM or synth state across suspend, and userspace may still need to reprepare streams or reload onboard sample data after resume. Open rawmidi substreams are restored only to a usable post-resume state. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260406-b4-alsa-gus-isa-pm-v1-1-b6829a7457cd@gmail.com --- include/sound/gus.h | 8 +++++ sound/isa/gus/gus_dma.c | 33 +++++++++++++++++++++ sound/isa/gus/gus_main.c | 36 +++++++++++++++++++++++ sound/isa/gus/gus_pcm.c | 7 +++-- sound/isa/gus/gus_reset.c | 62 ++++++++++++++++++++++++++++++++------- sound/isa/gus/gus_timer.c | 14 +++++++++ sound/isa/gus/gus_uart.c | 47 +++++++++++++++++++++++++++++ 7 files changed, 194 insertions(+), 13 deletions(-) diff --git a/include/sound/gus.h b/include/sound/gus.h index 321ae93625ebd9..3feb42627de1eb 100644 --- a/include/sound/gus.h +++ b/include/sound/gus.h @@ -536,6 +536,7 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * block, int atomic, int synth); +void snd_gf1_dma_suspend(struct snd_gus_card *gus); /* gus_volume.c */ @@ -552,6 +553,8 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice); int snd_gf1_start(struct snd_gus_card * gus); int snd_gf1_stop(struct snd_gus_card * gus); +int snd_gf1_suspend(struct snd_gus_card *gus); +int snd_gf1_resume(struct snd_gus_card *gus); /* gus_mixer.c */ @@ -572,6 +575,8 @@ int snd_gus_create(struct snd_card *card, int effect, struct snd_gus_card ** rgus); int snd_gus_initialize(struct snd_gus_card * gus); +int snd_gus_suspend(struct snd_gus_card *gus); +int snd_gus_resume(struct snd_gus_card *gus); /* gus_irq.c */ @@ -583,6 +588,8 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus); /* gus_uart.c */ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device); +void snd_gf1_uart_suspend(struct snd_gus_card *gus); +void snd_gf1_uart_resume(struct snd_gus_card *gus); /* gus_dram.c */ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr, @@ -593,5 +600,6 @@ int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr, /* gus_timer.c */ void snd_gf1_timers_init(struct snd_gus_card *gus); void snd_gf1_timers_done(struct snd_gus_card *gus); +void snd_gf1_timers_resume(struct snd_gus_card *gus); #endif /* __SOUND_GUS_H */ diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index ffc69e26227eb7..30bd76eee96e68 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -173,6 +173,39 @@ int snd_gf1_dma_done(struct snd_gus_card * gus) return 0; } +void snd_gf1_dma_suspend(struct snd_gus_card *gus) +{ + struct snd_gf1_dma_block *block; + + guard(mutex)(&gus->dma_mutex); + if (!gus->gf1.dma_shared) + return; + + snd_dma_disable(gus->gf1.dma1); + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + gus->gf1.dma_ack = NULL; + gus->gf1.dma_private_data = NULL; + + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + if (block->ack) + block->ack(gus, block->private_data); + kfree(block); + } + + gus->gf1.dma_data_pcm_last = NULL; + gus->gf1.dma_data_synth_last = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; +} + int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block * __block, int atomic, diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index b2b189c8356996..6adf8b698e2bcd 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -404,6 +404,42 @@ int snd_gus_initialize(struct snd_gus_card *gus) return 0; } +int snd_gus_suspend(struct snd_gus_card *gus) +{ + int err; + + if (gus->pcm) { + err = snd_pcm_suspend_all(gus->pcm); + if (err < 0) + return err; + } + + err = snd_gf1_suspend(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D3hot); + return 0; +} +EXPORT_SYMBOL(snd_gus_suspend); + +int snd_gus_resume(struct snd_gus_card *gus) +{ + int err; + + err = snd_gus_init_dma_irq(gus, 1); + if (err < 0) + return err; + + err = snd_gf1_resume(gus); + if (err < 0) + return err; + + snd_power_change_state(gus->card, SNDRV_CTL_POWER_D0); + return 0; +} +EXPORT_SYMBOL(snd_gus_resume); + /* gus_io.c */ EXPORT_SYMBOL(snd_gf1_delay); EXPORT_SYMBOL(snd_gf1_write8); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index caf371897b7807..a0757e1ede465c 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -471,7 +471,8 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { snd_gf1_pcm_trigger_up(substream); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { scoped_guard(spinlock, &pcmp->lock) { pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; } @@ -558,7 +559,8 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_START) { val = gus->gf1.pcm_rcntrl_reg; - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_SUSPEND) { val = 0; } else { return -EINVAL; @@ -856,4 +858,3 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) return 0; } - diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index a7a3e764bb77cf..998fa245708c2e 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -263,11 +264,18 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice) private_free(voice); } -/* - * call this function only by start of driver - */ +static void snd_gf1_init_software_state(struct snd_gus_card *gus) +{ + unsigned int i; -int snd_gf1_start(struct snd_gus_card * gus) + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } +} + +static void snd_gf1_hw_start(struct snd_gus_card *gus, bool initial) { unsigned int i; @@ -277,14 +285,14 @@ int snd_gf1_start(struct snd_gus_card * gus) udelay(160); snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); - for (i = 0; i < 32; i++) { - gus->gf1.voices[i].number = i; - snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + if (initial) { + snd_gf1_init_software_state(gus); + snd_gf1_uart_cmd(gus, 0x03); + } else { + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); } - snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ - if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); @@ -293,6 +301,8 @@ int snd_gf1_start(struct snd_gus_card * gus) snd_gf1_select_active_voices(gus); snd_gf1_delay(gus); gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + gus->gf1.hw_lfo = 0; + gus->gf1.sw_lfo = 0; /* initialize LFOs & clear LFOs memory */ if (gus->gf1.enh_mode && gus->gf1.memory) { gus->gf1.hw_lfo = 1; @@ -321,7 +331,15 @@ int snd_gf1_start(struct snd_gus_card * gus) outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); } +} +int snd_gf1_start(struct snd_gus_card *gus) +{ + /* + * Probe-time startup initializes both GF1 hardware and the + * software state that suspend/resume keeps across PM cycles. + */ + snd_gf1_hw_start(gus, true); snd_gf1_timers_init(gus); snd_gf1_look_regs(gus); snd_gf1_mem_init(gus); @@ -357,3 +375,27 @@ int snd_gf1_stop(struct snd_gus_card * gus) return 0; } + +int snd_gf1_suspend(struct snd_gus_card *gus) +{ + snd_gf1_dma_suspend(gus); + snd_gf1_uart_suspend(gus); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + snd_gf1_stop_voices(gus, 0, 31); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); + snd_dma_disable(gus->gf1.dma2); + + return 0; +} + +int snd_gf1_resume(struct snd_gus_card *gus) +{ + snd_gf1_hw_start(gus, false); + snd_gf1_timers_resume(gus); + snd_gf1_uart_resume(gus); + + return 0; +} diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index e3a8847e02cf1a..14dcde138bc74d 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -178,3 +178,17 @@ void snd_gf1_timers_done(struct snd_gus_card * gus) gus->gf1.timer2 = NULL; } } + +void snd_gf1_timers_resume(struct snd_gus_card *gus) +{ + if (gus->gf1.timer1) { + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + if (gus->gf1.timer_enabled & 4) + snd_gf1_timer1_start(gus->gf1.timer1); + } + if (gus->gf1.timer2) { + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + if (gus->gf1.timer_enabled & 8) + snd_gf1_timer2_start(gus->gf1.timer2); + } +} diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 770d8f3e4cfff6..25057a5a81b0a1 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -232,3 +232,50 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) gus->midi_uart = rmidi; return err; } + +void snd_gf1_uart_suspend(struct snd_gus_card *gus) +{ + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + outb(0x03, GUSP(gus, MIDICTRL)); +} + +void snd_gf1_uart_resume(struct snd_gus_card *gus) +{ + unsigned short uart_cmd; + bool active; + int i; + + scoped_guard(spinlock_irqsave, &gus->uart_cmd_lock) { + active = gus->midi_substream_input || gus->midi_substream_output; + } + if (!active) + return; + + /* snd_gf1_hw_start() already left MIDICTRL in reset. */ + usleep_range(160, 200); + + guard(spinlock_irqsave)(&gus->uart_cmd_lock); + if (!gus->midi_substream_input && !gus->midi_substream_output) + return; + + if (gus->midi_substream_output) + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + if (gus->midi_substream_input) + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + + if (!gus->uart_enable) + return; + + uart_cmd = gus->gf1.uart_cmd; + snd_gf1_uart_cmd(gus, 0x00); + + if (gus->midi_substream_input) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); + if (i >= 1000) + dev_err(gus->card->dev, + "gus midi uart resume - cleanup error\n"); + } + + snd_gf1_uart_cmd(gus, uart_cmd); +} From d9bfa935a9855664f5cea12131fa809fd56ff82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 6 Apr 2026 00:20:04 -0300 Subject: [PATCH 488/700] ALSA: gusclassic: add ISA suspend and resume callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gusclassic still leaves its ISA PM callbacks disabled because the shared GF1 core had no suspend and resume path suitable for PM recovery. Wire the driver up to the new shared GUS suspend and resume helpers so a suspend/resume cycle restores usable GF1 operation without rerunning probe-only detection or tearing down the runtime bookkeeping kept by the card instance. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260406-b4-alsa-gus-isa-pm-v1-2-b6829a7457cd@gmail.com --- sound/isa/gus/gusclassic.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c index 101202acefb366..363c819ced89a2 100644 --- a/sound/isa/gus/gusclassic.c +++ b/sound/isa/gus/gusclassic.c @@ -145,6 +145,7 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n) error = snd_gusclassic_create(card, dev, n, &gus); if (error < 0) return error; + card->private_data = gus; error = snd_gusclassic_detect(gus); if (error < 0) @@ -193,11 +194,29 @@ static int snd_gusclassic_probe(struct device *dev, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_gusclassic_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + + return snd_gus_suspend(card->private_data); +} + +static int snd_gusclassic_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + + return snd_gus_resume(card->private_data); +} +#endif + static struct isa_driver snd_gusclassic_driver = { .match = snd_gusclassic_match, .probe = snd_gusclassic_probe, -#if 0 /* FIXME */ +#ifdef CONFIG_PM .suspend = snd_gusclassic_suspend, + .resume = snd_gusclassic_resume, #endif .driver = { .name = DEV_NAME From 7da8af2541d01ce1a7ad5efbb3fee8567fdfc959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 6 Apr 2026 00:20:05 -0300 Subject: [PATCH 489/700] ALSA: gusextreme: add ISA suspend and resume callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gusextreme still leaves its ISA PM callbacks disabled because the shared GF1 core had no suspend and resume path suitable for PM recovery. Resume on this board needs one extra step before the shared GF1 path can touch the chip again: the ES1688 side must restore the GF1 routing. Split that routing sequence into a helper, reuse it for probe and resume, reset the ES1688 side first on resume, and then wire the driver up to the shared GUS PM helpers. This restores usable post-resume GF1 operation on GUS Extreme without rerunning probe-only detection in the shared GF1 path. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260406-b4-alsa-gus-isa-pm-v1-3-b6829a7457cd@gmail.com --- sound/isa/gus/gusextreme.c | 57 ++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index ed921b89b00ab1..0984731740c45b 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -44,6 +44,11 @@ static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; +struct snd_gusextreme { + struct snd_es1688 es1688; + struct snd_gus_card *gus; +}; + module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); module_param_array(id, charp, NULL, 0444); @@ -142,17 +147,15 @@ static int snd_gusextreme_gus_card_create(struct snd_card *card, 0, channels[n], pcm_channels[n], 0, rgus); } -static int snd_gusextreme_detect(struct snd_gus_card *gus, - struct snd_es1688 *es1688) +static void snd_gusextreme_enable_gf1(struct snd_gus_card *gus, + struct snd_es1688 *es1688) { - unsigned char d; - /* * This is main stuff - enable access to GF1 chip... * I'm not sure, if this will work for card which have * ES1688 chip in another place than 0x220. - * - * I used reverse-engineering in DOSEMU. [--jk] + * + * I used reverse-engineering in DOSEMU. [--jk] * * ULTRINIT.EXE: * 0x230 = 0,2,3 @@ -172,7 +175,14 @@ static int snd_gusextreme_detect(struct snd_gus_card *gus, outb(0, 0x201); outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); } +} + +static int snd_gusextreme_detect(struct snd_gus_card *gus, + struct snd_es1688 *es1688) +{ + unsigned char d; + snd_gusextreme_enable_gf1(gus, es1688); udelay(100); snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ @@ -223,16 +233,18 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) { struct snd_card *card; struct snd_gus_card *gus; + struct snd_gusextreme *gusextreme; struct snd_es1688 *es1688; struct snd_opl3 *opl3; int error; error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, - sizeof(struct snd_es1688), &card); + sizeof(*gusextreme), &card); if (error < 0) return error; - es1688 = card->private_data; + gusextreme = card->private_data; + es1688 = &gusextreme->es1688; if (mpu_port[n] == SNDRV_AUTO_PORT) mpu_port[n] = 0; @@ -250,6 +262,7 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) error = snd_gusextreme_gus_card_create(card, dev, n, &gus); if (error < 0) return error; + gusextreme->gus = gus; error = snd_gusextreme_detect(gus, es1688); if (error < 0) @@ -321,10 +334,36 @@ static int snd_gusextreme_probe(struct device *dev, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_gusextreme_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusextreme *gusextreme = card->private_data; + + return snd_gus_suspend(gusextreme->gus); +} + +static int snd_gusextreme_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusextreme *gusextreme = card->private_data; + int err; + + err = snd_es1688_reset(&gusextreme->es1688); + if (err < 0) + return err; + + snd_gusextreme_enable_gf1(gusextreme->gus, &gusextreme->es1688); + usleep_range(100, 200); + return snd_gus_resume(gusextreme->gus); +} +#endif + static struct isa_driver snd_gusextreme_driver = { .match = snd_gusextreme_match, .probe = snd_gusextreme_probe, -#if 0 /* FIXME */ +#ifdef CONFIG_PM .suspend = snd_gusextreme_suspend, .resume = snd_gusextreme_resume, #endif From 1b64e52380abfd368baa8a53d73336ffcd52c0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 6 Apr 2026 00:20:06 -0300 Subject: [PATCH 490/700] ALSA: gusmax: add ISA suspend and resume callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gusmax still leaves its ISA PM callbacks disabled even though the shared GF1 suspend and resume path now exists. This board needs one extra piece of PM glue around the shared GF1 helpers. The attached WSS codec has its own register image that must be saved and restored across suspend, and the MAX control register must be rewritten on resume before the codec and GF1 sides are brought back. Use the existing wss->suspend() and wss->resume() hooks for the codec, then wire the driver up to the shared GUS suspend and resume helpers for the GF1 side. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260406-b4-alsa-gus-isa-pm-v1-4-b6829a7457cd@gmail.com --- sound/isa/gus/gusmax.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index b572411c442289..f1fd7ff2121ded 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -328,12 +328,38 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) return 0; } +#ifdef CONFIG_PM +static int snd_gusmax_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusmax *maxcard = card->private_data; + + maxcard->wss->suspend(maxcard->wss); + return snd_gus_suspend(maxcard->gus); +} + +static int snd_gusmax_resume(struct device *dev, unsigned int n) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct snd_gusmax *maxcard = card->private_data; + + /* Restore the board routing latch before resuming the codec and GF1. */ + outb(maxcard->gus->max_cntrl_val, GUSP(maxcard->gus, MAXCNTRLPORT)); + maxcard->wss->resume(maxcard->wss); + return snd_gus_resume(maxcard->gus); +} +#endif + #define DEV_NAME "gusmax" static struct isa_driver snd_gusmax_driver = { .match = snd_gusmax_match, .probe = snd_gusmax_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_gusmax_suspend, + .resume = snd_gusmax_resume, +#endif .driver = { .name = DEV_NAME }, From 247d1c13992d2c501e2e020e84d9d2920e11bf78 Mon Sep 17 00:00:00 2001 From: Sheetal Date: Mon, 6 Apr 2026 09:05:47 +0000 Subject: [PATCH 491/700] ASoC: rt5640: Handle 0Hz sysclk during stream shutdown Commit 2458adb8f92a ("SoC: simple-card-utils: set 0Hz to sysclk when shutdown") sends a 0Hz sysclk request during stream shutdown to clear codec rate constraints. The rt5640 codec forwards this 0Hz to clk_set_rate(), which can cause clock controller firmware faults on platforms where MCLK is SoC-driven (e.g. Tegra) and 0Hz falls below the hardware minimum rate. Handle the 0Hz case by clearing the internal sysclk state and returning early, avoiding the invalid clk_set_rate() call. Signed-off-by: Sheetal Link: https://patch.msgid.link/20260406090547.988966-1-sheetal@nvidia.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index db2222e6f2e758..f6c6294e158805 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1838,6 +1838,11 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, unsigned int pll_bit = 0; int ret; + if (!freq) { + rt5640->sysclk = 0; + return 0; + } + switch (clk_id) { case RT5640_SCLK_S_MCLK: ret = clk_set_rate(rt5640->mclk, freq); From b481eabe5a193ba8499f446c2ab7e0ac042f8776 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Thu, 26 Mar 2026 17:05:31 +0530 Subject: [PATCH 492/700] ASoC: qcom: audioreach: explicitly enable speaker protection modules Speaker protection and VI feedback modules are disabled by default. Explicitly enable them when configuring speaker protection. Fixes: 3e43a8c033c3 ("ASoC: qcom: audioreach: Add support for VI Sense module") Fixes: 0db76f5b2235 ("ASoC: qcom: audioreach: Add support for Speaker Protection module") Signed-off-by: Ravi Hothi Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260326113531.3144998-1-ravi.hothi@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index b2975eebab717c..0eff5e572573f4 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1365,9 +1365,14 @@ int audioreach_set_media_format(struct q6apm_graph *graph, case MODULE_ID_SPEAKER_PROTECTION: rc = audioreach_speaker_protection(graph, module, PARAM_ID_SP_OP_MODE_NORMAL); + if (!rc) + rc = audioreach_module_enable(graph, module, true); + break; case MODULE_ID_SPEAKER_PROTECTION_VI: rc = audioreach_speaker_protection_vi(graph, module, cfg); + if (!rc) + rc = audioreach_module_enable(graph, module, true); break; default: From 5075d08e72af38b829fb71800c2a5be61d0b2e2c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 6 Apr 2026 01:44:35 +0200 Subject: [PATCH 493/700] ASoC: dt-bindings: ti,tas2552: Add sound-dai-cells Add missing sound-sai-cells for this codec into schema. At the same time, drop trailing spaces from description. Fixes: 506e0825a4c9 ("ASoC: dt-bindings: Convert ti,tas2552 to DT schema") Signed-off-by: Marek Vasut Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260405234502.154227-1-marex@nabladev.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ti,tas2552.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/ti,tas2552.yaml b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml index 10369aa5f0a86c..85e3ebd2acd84a 100644 --- a/Documentation/devicetree/bindings/sound/ti,tas2552.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2552.yaml @@ -12,8 +12,8 @@ maintainers: - Baojun Xu description: > - The TAS2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or - use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, + The TAS2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or + use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. For system integration the dt-bindings/sound/tas2552.h header file provides @@ -34,6 +34,9 @@ properties: maxItems: 1 description: gpio pin to enable/disable the device + '#sound-dai-cells': + const: 0 + required: - compatible - reg @@ -41,7 +44,10 @@ required: - iovdd-supply - avdd-supply -additionalProperties: false +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false examples: - | @@ -54,6 +60,7 @@ examples: audio-codec@41 { compatible = "ti,tas2552"; reg = <0x41>; + #sound-dai-cells = <0>; vbat-supply = <®_vbat>; iovdd-supply = <®_iovdd>; avdd-supply = <®_avdd>; From 80a7916ca2d902b329a40e529c4c4131ae6ff273 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Thu, 2 Apr 2026 21:03:25 -0400 Subject: [PATCH 494/700] ASoC: amd: acp: add Lenovo P16s G5 AMD quirk for legacy SDW machine Add a DMI quirk entry for Lenovo P16s G5 AMD to use ASOC_SDW_ACP_DMIC. Needed to allow the microphone to work on this platform Signed-off-by: Mark Pearson Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260403010336.1223078-1-mpearson-lenovo@squebb.ca Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 6388cd7cb28ebe..92749dc17df86e 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -111,6 +111,22 @@ static const struct dmi_system_id soc_sdw_quirk_table[] = { }, .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), }, + { + .callback = soc_sdw_quirk_cb, + .matches = { /* Lenovo P16s G5 AMD */ + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_SKU, "21XG"), + }, + .driver_data = (void *)(ASOC_SDW_ACP_DMIC), + }, + { + .callback = soc_sdw_quirk_cb, + .matches = { /* Lenovo P16s G5 AMD */ + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_SKU, "21XH"), + }, + .driver_data = (void *)(ASOC_SDW_ACP_DMIC), + }, { .callback = soc_sdw_quirk_cb, .matches = { From 2c4fdd055f92a2fc8602dcd88bcea08c374b7e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 25 Mar 2026 17:05:11 -0300 Subject: [PATCH 495/700] ASoC: SOF: compress: return the configured codec from get_params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SOF compressed offload path accepts codec parameters in sof_compr_set_params() and forwards them to firmware as extended data in the SOF IPC stream params message. However, sof_compr_get_params() still returns success without filling the snd_codec structure. Since the compress core allocates that structure zeroed and copies it back to userspace on success, SNDRV_COMPRESS_GET_PARAMS returns an all-zero codec description even after the stream has been configured successfully. The stale TODO in this callback conflates get_params() with capability discovery. Supported codec enumeration belongs in get_caps() and get_codec_caps(). get_params() should report the current codec settings. Cache the codec accepted by sof_compr_set_params() in the per-stream SOF compress state and return it from sof_compr_get_params(). Fixes: 6324cf901e14 ("ASoC: SOF: compr: Add compress ops implementation") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260325-sof-compr-get-params-v1-1-0758815f13c7@gmail.com Signed-off-by: Mark Brown --- sound/soc/sof/compress.c | 8 +++++--- sound/soc/sof/sof-priv.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 3dbc05bf3b7917..f4c3e10e6344d4 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -255,6 +255,7 @@ static int sof_compr_set_params(struct snd_soc_component *component, sstream->sampling_rate = params->codec.sample_rate; sstream->channels = params->codec.ch_out; sstream->sample_container_bytes = pcm->params.sample_container_bytes; + sstream->codec_params = params->codec; spcm->prepared[cstream->direction] = true; @@ -267,9 +268,10 @@ static int sof_compr_set_params(struct snd_soc_component *component, static int sof_compr_get_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_codec *params) { - /* TODO: we don't query the supported codecs for now, if the - * application asks for an unsupported codec the set_params() will fail. - */ + struct sof_compr_stream *sstream = cstream->runtime->private_data; + + *params = sstream->codec_params; + return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 693d063830faed..38753b088fc1dd 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -111,6 +112,7 @@ struct sof_compr_stream { u32 sampling_rate; u16 channels; u16 sample_container_bytes; + struct snd_codec codec_params; size_t posn_offset; }; From 4e53116437e919c4b9a9d95fb73ae14fe0cfc8f9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Mar 2026 14:14:49 +0000 Subject: [PATCH 496/700] ASoC: SDCA: Fix errors in IRQ cleanup IRQs are enabled through sdca_irq_populate() from component probe using devm_request_threaded_irq(), this however means the IRQs can persist if the sound card is torn down. Some of the IRQ handlers store references to the card and the kcontrols which can then fail. Some detail of the crash was explained in [1]. Generally it is not advised to use devm outside of bus probe, so the code is updated to not use devm. The IRQ requests are not moved to bus probe time as it makes passing the snd_soc_component into the IRQs very awkward and would the require a second step once the component is available, so it is simpler to just register the IRQs at this point, even though that necessitates some manual cleanup. Link: https://lore.kernel.org/linux-sound/20260310183829.2907805-1-gaggery.tsai@intel.com/ [1] Fixes: b126394d9ec6 ("ASoC: SDCA: Generic interrupt support") Reported-by: Gaggery Tsai Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260316141449.2950215-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 5 ++ sound/soc/sdca/sdca_class_function.c | 9 ++++ sound/soc/sdca/sdca_interrupts.c | 77 ++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 9bcb5d8fd592bf..b47003c3d26ef4 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -69,6 +69,8 @@ struct sdca_interrupt_info { int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *interrupt_info, int sdca_irq, const char *name, irq_handler_t handler, void *data); +void sdca_irq_free(struct device *dev, struct sdca_interrupt_info *interrupt_info, + int sdca_irq, const char *name, void *data); int sdca_irq_data_populate(struct device *dev, struct regmap *function_regmap, struct snd_soc_component *component, struct sdca_function_data *function, @@ -81,6 +83,9 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *function_regmap, int sdca_irq_populate(struct sdca_function_data *function, struct snd_soc_component *component, struct sdca_interrupt_info *info); +void sdca_irq_cleanup(struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info); struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, struct regmap *regmap, int irq); diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 98fd3fd1052b42..fe404d769c78c8 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -198,6 +198,14 @@ static int class_function_component_probe(struct snd_soc_component *component) return sdca_irq_populate(drv->function, component, core->irq_info); } +static void class_function_component_remove(struct snd_soc_component *component) +{ + struct class_function_drv *drv = snd_soc_component_get_drvdata(component); + struct sdca_class_drv *core = drv->core; + + sdca_irq_cleanup(drv->function, component, core->irq_info); +} + static int class_function_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *d) { @@ -209,6 +217,7 @@ static int class_function_set_jack(struct snd_soc_component *component, static const struct snd_soc_component_driver class_function_component_drv = { .probe = class_function_component_probe, + .remove = class_function_component_remove, .endianness = 1, }; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 95b1ab4ba1b036..1838dabcdf6047 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -252,8 +252,7 @@ static int sdca_irq_request_locked(struct device *dev, if (irq < 0) return irq; - ret = devm_request_threaded_irq(dev, irq, NULL, handler, - IRQF_ONESHOT, name, data); + ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, data); if (ret) return ret; @@ -264,6 +263,22 @@ static int sdca_irq_request_locked(struct device *dev, return 0; } +static void sdca_irq_free_locked(struct device *dev, struct sdca_interrupt_info *info, + int sdca_irq, const char *name, void *data) +{ + int irq; + + irq = regmap_irq_get_virq(info->irq_data, sdca_irq); + if (irq < 0) + return; + + free_irq(irq, data); + + info->irqs[sdca_irq].irq = 0; + + dev_dbg(dev, "freed irq %d for %s\n", irq, name); +} + /** * sdca_irq_request - request an individual SDCA interrupt * @dev: Pointer to the struct device against which things should be allocated. @@ -302,6 +317,30 @@ int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, } EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA"); +/** + * sdca_irq_free - free an individual SDCA interrupt + * @dev: Pointer to the struct device. + * @info: Pointer to the interrupt information structure. + * @sdca_irq: SDCA interrupt position. + * @name: Name to be given to the IRQ. + * @data: Private data pointer that will be passed to the handler. + * + * Typically this is handled internally by sdca_irq_cleanup, however if + * a device requires custom IRQ handling this can be called manually before + * calling sdca_irq_cleanup, which will then skip that IRQ whilst processing. + */ +void sdca_irq_free(struct device *dev, struct sdca_interrupt_info *info, + int sdca_irq, const char *name, void *data) +{ + if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS) + return; + + guard(mutex)(&info->irq_lock); + + sdca_irq_free_locked(dev, info, sdca_irq, name, data); +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_free, "SND_SOC_SDCA"); + /** * sdca_irq_data_populate - Populate common interrupt data * @dev: Pointer to the Function device. @@ -328,8 +367,8 @@ int sdca_irq_data_populate(struct device *dev, struct regmap *regmap, if (!dev) return -ENODEV; - name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name, - entity->label, control->label); + name = kasprintf(GFP_KERNEL, "%s %s %s", function->desc->name, + entity->label, control->label); if (!name) return -ENOMEM; @@ -516,6 +555,36 @@ int sdca_irq_populate(struct sdca_function_data *function, } EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); +/** + * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function + * @function: Pointer to the SDCA Function. + * @component: Pointer to the ASoC component for the Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * Typically this would be called from the driver for a single SDCA Function. + */ +void sdca_irq_cleanup(struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info) +{ + struct device *dev = component->dev; + int i; + + guard(mutex)(&info->irq_lock); + + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + struct sdca_interrupt *interrupt = &info->irqs[i]; + + if (interrupt->function != function || !interrupt->irq) + continue; + + sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); + + kfree(interrupt->name); + } +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); + /** * sdca_irq_allocate - allocate an SDCA interrupt structure for a device * @sdev: Device pointer against which things should be allocated. From 86facd80a2a37536937f06de637abf9e8cabdb4b Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Thu, 2 Apr 2026 14:45:30 +0800 Subject: [PATCH 497/700] ASoC: SOF: Intel: Fix endpoint index if endpoints are missing In case of missing endpoints, the sequential numbering will cause wrong mapping. Instead, assign the original DAI index from codec_info_list. Fixes: 5226d19d4cae ("ASoC: SOF: Intel: use sof_sdw as default SDW machine driver") Signed-off-by: Maciej Strozek Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260402064531.2287261-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c0cc7d3ce5262b..57632b4641179d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1203,7 +1203,7 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, codec_info_list[i].dais[j].dai_type)) continue; - endpoints[ep_index].num = ep_index; + endpoints[ep_index].num = j; if (codec_info_list[i].dais[j].dai_type == SOC_SDW_DAI_TYPE_AMP) { /* Assume all amp are aggregated */ endpoints[ep_index].aggregated = 1; From 1de6ddcddc954a69f96b1c23205e03ddd603e3c8 Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Thu, 2 Apr 2026 14:45:31 +0800 Subject: [PATCH 498/700] ASoC: SOF: Intel: fix iteration in is_endpoint_present() is_endpoint_present() iterates over sdca_data.num_functions, but checks the dai_type according to codec info list, which will cause problems if not all endpoints from the codec info list are present. Make sure the type of actually present functions is compared against target dai_type. Fixes: 5226d19d4cae ("ASoC: SOF: Intel: use sof_sdw as default SDW machine driver") Signed-off-by: Maciej Strozek Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260402064531.2287261-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 57632b4641179d..8a240dcb7fcb39 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1133,8 +1133,7 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) -static bool is_endpoint_present(struct sdw_slave *sdw_device, - struct asoc_sdw_codec_info *dai_info, int dai_type) +static bool is_endpoint_present(struct sdw_slave *sdw_device, int dai_type) { int i; @@ -1145,7 +1144,7 @@ static bool is_endpoint_present(struct sdw_slave *sdw_device, } for (i = 0; i < sdw_device->sdca_data.num_functions; i++) { - if (dai_type == dai_info->dais[i].dai_type) + if (dai_type == asoc_sdw_get_dai_type(sdw_device->sdca_data.function[i].type)) return true; } dev_dbg(&sdw_device->dev, "Endpoint DAI type %d not found\n", dai_type); @@ -1199,8 +1198,7 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, } for (j = 0; j < codec_info_list[i].dai_num; j++) { /* Check if the endpoint is present by the SDCA DisCo table */ - if (!is_endpoint_present(sdw_device, &codec_info_list[i], - codec_info_list[i].dais[j].dai_type)) + if (!is_endpoint_present(sdw_device, codec_info_list[i].dais[j].dai_type)) continue; endpoints[ep_index].num = j; From d78ddeb8938a366aabfabf60255c1a94de8d8ea1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Apr 2026 05:51:51 +0000 Subject: [PATCH 499/700] ASoC: soc.h: remove unused card->pmdown_time commit f0fba2ad1b6b ("ASoC: multi-component - ASoC Multi-Component Support") has replaced "card->pmdown_time" to "rtd->pmdown_time". card->pmdown_time has been not used this 15 years. Let's remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87eckstz49.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index f70edd9c23b271..5e3eb617d8323a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1027,8 +1027,6 @@ struct snd_soc_card { void (*remove_dai_link)(struct snd_soc_card *, struct snd_soc_dai_link *link); - long pmdown_time; - /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; /* predefined links only */ int num_links; /* predefined links only */ From 82d8701b2c930d0e96b0dbc9115a218d791cb0d2 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Mon, 6 Apr 2026 21:17:28 +0800 Subject: [PATCH 500/700] batman-adv: hold claim backbone gateways by reference batadv_bla_add_claim() can replace claim->backbone_gw and drop the old gateway's last reference while readers still follow the pointer. The netlink claim dump path dereferences claim->backbone_gw->orig and takes claim->backbone_gw->crc_lock without pinning the underlying backbone gateway. batadv_bla_check_claim() still has the same naked pointer access pattern. Reuse batadv_bla_claim_get_backbone_gw() in both readers so they operate on a stable gateway reference until the read-side work is complete. This keeps the dump and claim-check paths aligned with the lifetime rules introduced for the other BLA claim readers. Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Fixes: 04f3f5bf1883 ("batman-adv: add B.A.T.M.A.N. Dump BLA claims via netlink") Cc: stable@vger.kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Haoze Xie Signed-off-by: Ao Zhou Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 3dc791c15bf72e..648fa97ea913f5 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -2130,6 +2130,7 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, struct batadv_bla_claim *claim) { const u8 *primary_addr = primary_if->net_dev->dev_addr; + struct batadv_bla_backbone_gw *backbone_gw; u16 backbone_crc; bool is_own; void *hdr; @@ -2145,32 +2146,35 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, genl_dump_check_consistent(cb, hdr); - is_own = batadv_compare_eth(claim->backbone_gw->orig, - primary_addr); + backbone_gw = batadv_bla_claim_get_backbone_gw(claim); + + is_own = batadv_compare_eth(backbone_gw->orig, primary_addr); - spin_lock_bh(&claim->backbone_gw->crc_lock); - backbone_crc = claim->backbone_gw->crc; - spin_unlock_bh(&claim->backbone_gw->crc_lock); + spin_lock_bh(&backbone_gw->crc_lock); + backbone_crc = backbone_gw->crc; + spin_unlock_bh(&backbone_gw->crc_lock); if (is_own) if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { genlmsg_cancel(msg, hdr); - goto out; + goto put_backbone_gw; } if (nla_put(msg, BATADV_ATTR_BLA_ADDRESS, ETH_ALEN, claim->addr) || nla_put_u16(msg, BATADV_ATTR_BLA_VID, claim->vid) || nla_put(msg, BATADV_ATTR_BLA_BACKBONE, ETH_ALEN, - claim->backbone_gw->orig) || + backbone_gw->orig) || nla_put_u16(msg, BATADV_ATTR_BLA_CRC, backbone_crc)) { genlmsg_cancel(msg, hdr); - goto out; + goto put_backbone_gw; } genlmsg_end(msg, hdr); ret = 0; +put_backbone_gw: + batadv_backbone_gw_put(backbone_gw); out: return ret; } @@ -2448,6 +2452,7 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb) bool batadv_bla_check_claim(struct batadv_priv *bat_priv, u8 *addr, unsigned short vid) { + struct batadv_bla_backbone_gw *backbone_gw; struct batadv_bla_claim search_claim; struct batadv_bla_claim *claim = NULL; struct batadv_hard_iface *primary_if = NULL; @@ -2470,9 +2475,13 @@ bool batadv_bla_check_claim(struct batadv_priv *bat_priv, * return false. */ if (claim) { - if (!batadv_compare_eth(claim->backbone_gw->orig, + backbone_gw = batadv_bla_claim_get_backbone_gw(claim); + + if (!batadv_compare_eth(backbone_gw->orig, primary_if->net_dev->dev_addr)) ret = false; + + batadv_backbone_gw_put(backbone_gw); batadv_claim_put(claim); } From f58df566524ebcdfa394329c64f47e3c9257516e Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 17 Mar 2026 17:29:55 +0800 Subject: [PATCH 501/700] mm: filemap: fix nr_pages calculation overflow in filemap_map_pages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running stress-ng on my Arm64 machine with v7.0-rc3 kernel, I encountered some very strange crash issues showing up as "Bad page state": " [ 734.496287] BUG: Bad page state in process stress-ng-env pfn:415735fb [ 734.496427] page: refcount:0 mapcount:1 mapping:0000000000000000 index:0x4cf316 pfn:0x415735fb [ 734.496434] flags: 0x57fffe000000800(owner_2|node=1|zone=2|lastcpupid=0x3ffff) [ 734.496439] raw: 057fffe000000800 0000000000000000 dead000000000122 0000000000000000 [ 734.496440] raw: 00000000004cf316 0000000000000000 0000000000000000 0000000000000000 [ 734.496442] page dumped because: nonzero mapcount " After analyzing this page’s state, it is hard to understand why the mapcount is not 0 while the refcount is 0, since this page is not where the issue first occurred. By enabling the CONFIG_DEBUG_VM config, I can reproduce the crash as well and captured the first warning where the issue appears: " [ 734.469226] page: refcount:33 mapcount:0 mapping:00000000bef2d187 index:0x81a0 pfn:0x415735c0 [ 734.469304] head: order:5 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 734.469315] memcg:ffff000807a8ec00 [ 734.469320] aops:ext4_da_aops ino:100b6f dentry name(?):"stress-ng-mmaptorture-9397-0-2736200540" [ 734.469335] flags: 0x57fffe400000069(locked|uptodate|lru|head|node=1|zone=2|lastcpupid=0x3ffff) ...... [ 734.469364] page dumped because: VM_WARN_ON_FOLIO((_Generic((page + nr_pages - 1), const struct page *: (const struct folio *)_compound_head(page + nr_pages - 1), struct page *: (struct folio *)_compound_head(page + nr_pages - 1))) != folio) [ 734.469390] ------------[ cut here ]------------ [ 734.469393] WARNING: ./include/linux/rmap.h:351 at folio_add_file_rmap_ptes+0x3b8/0x468, CPU#90: stress-ng-mlock/9430 [ 734.469551] folio_add_file_rmap_ptes+0x3b8/0x468 (P) [ 734.469555] set_pte_range+0xd8/0x2f8 [ 734.469566] filemap_map_folio_range+0x190/0x400 [ 734.469579] filemap_map_pages+0x348/0x638 [ 734.469583] do_fault_around+0x140/0x198 ...... [ 734.469640] el0t_64_sync+0x184/0x188 " The code that triggers the warning is: "VM_WARN_ON_FOLIO(page_folio(page + nr_pages - 1) != folio, folio)", which indicates that set_pte_range() tried to map beyond the large folio’s size. By adding more debug information, I found that 'nr_pages' had overflowed in filemap_map_pages(), causing set_pte_range() to establish mappings for a range exceeding the folio size, potentially corrupting fields of pages that do not belong to this folio (e.g., page->_mapcount). After above analysis, I think the possible race is as follows: CPU 0 CPU 1 filemap_map_pages() ext4_setattr() //get and lock folio with old inode->i_size next_uptodate_folio() ....... //shrink the inode->i_size i_size_write(inode, attr->ia_size); //calculate the end_pgoff with the new inode->i_size file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1; end_pgoff = min(end_pgoff, file_end); ...... //nr_pages can be overflowed, cause xas.xa_index > end_pgoff end = folio_next_index(folio) - 1; nr_pages = min(end, end_pgoff) - xas.xa_index + 1; ...... //map large folio filemap_map_folio_range() ...... //truncate folios truncate_pagecache(inode, inode->i_size); To fix this issue, move the 'end_pgoff' calculation before next_uptodate_folio(), so the retrieved folio stays consistent with the file end to avoid 'nr_pages' calculation overflow. After this patch, the crash issue is gone. Link: https://lkml.kernel.org/r/1cf1ac59018fc647a87b0dad605d4056a71c14e4.1773739704.git.baolin.wang@linux.alibaba.com Fixes: 743a2753a02e ("filemap: cap PTE range to be created to allowed zero fill in folio_map_range()") Signed-off-by: Baolin Wang Reported-by: Yuanhe Shu Tested-by: Yuanhe Shu Acked-by: Kiryl Shutsemau (Meta) Acked-by: David Hildenbrand (Arm) Cc: Christian Brauner Cc: Daniel Gomez Cc: "Darrick J. Wong" Cc: Dave Chinner Cc: David Howells Cc: Hannes Reinecke Cc: Lorenzo Stoakes (Oracle) Cc: Luis Chamberalin Cc: Matthew Wilcox (Oracle) Cc: Pankaj Raghav Cc: Signed-off-by: Andrew Morton --- mm/filemap.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 406cef06b684a8..3c1e785542dde0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3883,14 +3883,19 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, unsigned int nr_pages = 0, folio_type; unsigned short mmap_miss = 0, mmap_miss_saved; + /* + * Recalculate end_pgoff based on file_end before calling + * next_uptodate_folio() to avoid races with concurrent + * truncation. + */ + file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1; + end_pgoff = min(end_pgoff, file_end); + rcu_read_lock(); folio = next_uptodate_folio(&xas, mapping, end_pgoff); if (!folio) goto out; - file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1; - end_pgoff = min(end_pgoff, file_end); - /* * Do not allow to map with PMD across i_size to preserve * SIGBUS semantics. From 307e0c5859b0aecc34180468b1aa76684adcf539 Mon Sep 17 00:00:00 2001 From: Leo Timmins Date: Wed, 25 Mar 2026 12:46:07 +0800 Subject: [PATCH 502/700] liveupdate: propagate file deserialization failures luo_session_deserialize() ignored the return value from luo_file_deserialize(). As a result, a session could be left partially restored even though the /dev/liveupdate open path treats deserialization failures as fatal. Propagate the error so a failed file deserialization aborts session deserialization instead of silently continuing. Link: https://lkml.kernel.org/r/20260325044608.8407-1-leotimmins1974@gmail.com Link: https://lkml.kernel.org/r/20260325044608.8407-2-leotimmins1974@gmail.com Fixes: 16cec0d26521 ("liveupdate: luo_session: add ioctls for file preservation") Signed-off-by: Leo Timmins Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Cc: Mike Rapoport Cc: Signed-off-by: Andrew Morton --- kernel/liveupdate/luo_session.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 78367729564067..25ae704d778734 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -558,8 +558,13 @@ int luo_session_deserialize(void) } scoped_guard(mutex, &session->mutex) { - luo_file_deserialize(&session->file_set, - &sh->ser[i].file_set_ser); + err = luo_file_deserialize(&session->file_set, + &sh->ser[i].file_set_ser); + } + if (err) { + pr_warn("Failed to deserialize files for session [%s] %pe\n", + session->name, ERR_PTR(err)); + return err; } } From cece9dc61daab6006d3ac9d36a0df2dd58fef18d Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Thu, 26 Mar 2026 14:51:27 -0700 Subject: [PATCH 503/700] mm: reinstate unconditional writeback start in balance_dirty_pages() Commit 64dd89ae01f2 ("mm/block/fs: remove laptop_mode") removed this unconditional writeback start from balance_dirty_pages(): if (unlikely(!writeback_in_progress(wb))) wb_start_background_writeback(wb); This logic needs to be reinstated to prevent performance regressions for strictlimited BDIs and memcg setups. The problem occurs because: a) For strictlimited BDIs, throttling is calculated using per-wb thresholds. The per-wb threshold can be exceeded even when the global dirty threshold was not exceeded (nr_dirty < gdtc->bg_thresh) b) For memcg-based throttling, memcg uses its own dirty count / thresholds and can trigger throttling even when the global threshold isn't exceeded Without the unconditional writeback start, IO is throttled as it waits for dirty pages to be written back but there is no writeback running. This leads to severe stalls. On fuse, buffered write performance dropped from 1400 MiB/s to 2000 KiB/s. Reinstate the unconditional writeback start so that writeback is guaranteed to be running whenever IO needs to be throttled. Link: https://lkml.kernel.org/r/20260326215127.3857682-2-joannelkoong@gmail.com Fixes: 64dd89ae01f2 ("mm/block/fs: remove laptop_mode") Signed-off-by: Joanne Koong Reviewed-by: Christoph Hellwig Reviewed-by: Jan Kara Acked-by: Johannes Weiner Cc: Matthew Wilcox (Oracle) Cc: Signed-off-by: Andrew Morton --- mm/page-writeback.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 601a5e048d1285..c1a4b32af1a7f9 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1858,6 +1858,27 @@ static int balance_dirty_pages(struct bdi_writeback *wb, break; } + /* + * Unconditionally start background writeback if it's not + * already in progress. We need to do this because the global + * dirty threshold check above (nr_dirty > gdtc->bg_thresh) + * doesn't account for these cases: + * + * a) strictlimit BDIs: throttling is calculated using per-wb + * thresholds. The per-wb threshold can be exceeded even when + * nr_dirty < gdtc->bg_thresh + * + * b) memcg-based throttling: memcg uses its own dirty count and + * thresholds and can trigger throttling even when global + * nr_dirty < gdtc->bg_thresh + * + * Writeback needs to be started else the writer stalls in the + * throttle loop waiting for dirty pages to be written back + * while no writeback is running. + */ + if (unlikely(!writeback_in_progress(wb))) + wb_start_background_writeback(wb); + mem_cgroup_flush_foreign(wb); /* From 0199390a6b92fc21860e1b858abf525c7e73b956 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 26 Mar 2026 17:32:22 -0700 Subject: [PATCH 504/700] mm/damon/sysfs: dealloc repeat_call_control if damon_call() fails damon_call() for repeat_call_control of DAMON_SYSFS could fail if somehow the kdamond is stopped before the damon_call(). It could happen, for example, when te damon context was made for monitroing of a virtual address processes, and the process is terminated immediately, before the damon_call() invocation. In the case, the dyanmically allocated repeat_call_control is not deallocated and leaked. Fix the leak by deallocating the repeat_call_control under the damon_call() failure. This issue is discovered by sashiko [1]. Link: https://lkml.kernel.org/r/20260327003224.55752-1-sj@kernel.org Link: https://lore.kernel.org/20260320020630.962-1-sj@kernel.org [1] Fixes: 04a06b139ec0 ("mm/damon/sysfs: use dynamically allocated repeat mode damon_call_control") Signed-off-by: SeongJae Park Cc: [6.17+] Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 6a44a2f3d8fc9d..eefa959aa30aea 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1670,7 +1670,8 @@ static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond) repeat_call_control->data = kdamond; repeat_call_control->repeat = true; repeat_call_control->dealloc_on_cancel = true; - damon_call(ctx, repeat_call_control); + if (damon_call(ctx, repeat_call_control)) + kfree(repeat_call_control); return err; } From 2ecbe06abf9bfb2261cd6464a6bc3a3615625402 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Mon, 30 Mar 2026 11:57:49 +0800 Subject: [PATCH 505/700] mm/memory_hotplug: maintain N_NORMAL_MEMORY during hotplug N_NORMAL_MEMORY is initialized from zone population at boot, but memory hotplug currently only updates N_MEMORY. As a result, a node that gains normal memory via hotplug can remain invisible to users iterating over N_NORMAL_MEMORY, while a node that loses its last normal memory can stay incorrectly marked as such. The most visible effect is that /sys/devices/system/node/has_normal_memory does not report a node even after that node has gained normal memory via hotplug. Also, list_lru-based shrinkers can undercount objects on such a node and may skip reclaim on that node entirely, which can lead to a higher memory footprint than expected. Restore N_NORMAL_MEMORY maintenance directly in online_pages() and offline_pages(). Set the bit when a node that currently lacks normal memory onlines pages into a zone <= ZONE_NORMAL, and clear it when offlining removes the last present pages from zones <= ZONE_NORMAL. This restores the intended semantics without bringing back the old status_change_nid_normal notifier plumbing which was removed in 8d2882a8edb8. Current users that benefit include list_lru, zswap, nfsd filecache, hugetlb_cgroup, and has_normal_memory sysfs reporting. Link: https://lkml.kernel.org/r/20260330035941.518186-1-hao.li@linux.dev Fixes: 8d2882a8edb8 ("mm,memory_hotplug: remove status_change_nid_normal and update documentation") Signed-off-by: Hao Li Reviewed-by: Harry Yoo (Oracle) Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Joshua Hahn Acked-by: David Hildenbrand (Arm) Cc: Oscar Salvador Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/memory_hotplug.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index bc805029da5175..05a47953ef21b5 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1209,6 +1209,13 @@ int online_pages(unsigned long pfn, unsigned long nr_pages, if (node_arg.nid >= 0) node_set_state(nid, N_MEMORY); + /* + * Check whether we are adding normal memory to the node for the first + * time. + */ + if (!node_state(nid, N_NORMAL_MEMORY) && zone_idx(zone) <= ZONE_NORMAL) + node_set_state(nid, N_NORMAL_MEMORY); + if (need_zonelists_rebuild) build_all_zonelists(NULL); @@ -1908,6 +1915,8 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages, unsigned long flags; char *reason; int ret; + unsigned long normal_pages = 0; + enum zone_type zt; /* * {on,off}lining is constrained to full memory sections (or more @@ -2055,6 +2064,17 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages, /* reinitialise watermarks and update pcp limits */ init_per_zone_wmark_min(); + /* + * Check whether this operation removes the last normal memory from + * the node. We do this before clearing N_MEMORY to avoid the possible + * transient "!N_MEMORY && N_NORMAL_MEMORY" state. + */ + if (zone_idx(zone) <= ZONE_NORMAL) { + for (zt = 0; zt <= ZONE_NORMAL; zt++) + normal_pages += pgdat->node_zones[zt].present_pages; + if (!normal_pages) + node_clear_state(node, N_NORMAL_MEMORY); + } /* * Make sure to mark the node as memory-less before rebuilding the zone * list. Otherwise this node would still appear in the fallback lists. From 894f99eb535edc4514f756818f3c4f688ba53a59 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Tue, 31 Mar 2026 18:08:11 +0000 Subject: [PATCH 506/700] mm/vma: fix memory leak in __mmap_region() commit 605f6586ecf7 ("mm/vma: do not leak memory when .mmap_prepare swaps the file") handled the success path by skipping get_file() via file_doesnt_need_get, but missed the error path. When /dev/zero is mmap'd with MAP_SHARED, mmap_zero_prepare() calls shmem_zero_setup_desc() which allocates a new shmem file to back the mapping. If __mmap_new_vma() subsequently fails, this replacement file is never fput()'d - the original is released by ksys_mmap_pgoff(), but nobody releases the new one. Add fput() for the swapped file in the error path. Reproducible with fault injection. FAULT_INJECTION: forcing a failure. name failslab, interval 1, probability 0, space 0, times 1 CPU: 2 UID: 0 PID: 366 Comm: syz.7.14 Not tainted 7.0.0-rc6 #2 PREEMPT(full) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: dump_stack_lvl+0x164/0x1f0 should_fail_ex+0x525/0x650 should_failslab+0xdf/0x140 kmem_cache_alloc_noprof+0x78/0x630 vm_area_alloc+0x24/0x160 __mmap_region+0xf6b/0x2660 mmap_region+0x2eb/0x3a0 do_mmap+0xc79/0x1240 vm_mmap_pgoff+0x252/0x4c0 ksys_mmap_pgoff+0xf8/0x120 __x64_sys_mmap+0x12a/0x190 do_syscall_64+0xa9/0x580 entry_SYSCALL_64_after_hwframe+0x76/0x7e kmemleak: 1 new suspected memory leaks (see /sys/kernel/debug/kmemleak) BUG: memory leak unreferenced object 0xffff8881118aca80 (size 360): comm "syz.7.14", pid 366, jiffies 4294913255 hex dump (first 32 bytes): 00 00 00 00 ad 4e ad de ff ff ff ff 00 00 00 00 .....N.......... ff ff ff ff ff ff ff ff c0 28 4d ae ff ff ff ff .........(M..... backtrace (crc db0f53bc): kmem_cache_alloc_noprof+0x3ab/0x630 alloc_empty_file+0x5a/0x1e0 alloc_file_pseudo+0x135/0x220 __shmem_file_setup+0x274/0x420 shmem_zero_setup_desc+0x9c/0x170 mmap_zero_prepare+0x123/0x140 __mmap_region+0xdda/0x2660 mmap_region+0x2eb/0x3a0 do_mmap+0xc79/0x1240 vm_mmap_pgoff+0x252/0x4c0 ksys_mmap_pgoff+0xf8/0x120 __x64_sys_mmap+0x12a/0x190 do_syscall_64+0xa9/0x580 entry_SYSCALL_64_after_hwframe+0x76/0x7e Found by syzkaller. Link: https://lkml.kernel.org/r/20260331180811.1333348-1-rhkrqnwk98@gmail.com Fixes: 605f6586ecf7 ("mm/vma: do not leak memory when .mmap_prepare swaps the file") Signed-off-by: Sechang Lim Reviewed-by: Lorenzo Stoakes (Oracle) Acked-by: Vlastimil Babka (SUSE) Cc: Jann Horn Cc: Liam Howlett Cc: Lorenzo Stoakes (Oracle) Cc: Pedro Falcato Cc: Signed-off-by: Andrew Morton --- mm/vma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mm/vma.c b/mm/vma.c index be64f781a3aa74..c8df5f561ad7d1 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -2781,6 +2781,13 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, if (map.charged) vm_unacct_memory(map.charged); abort_munmap: + /* + * This indicates that .mmap_prepare has set a new file, differing from + * desc->vm_file. But since we're aborting the operation, only the + * original file will be cleaned up. Ensure we clean up both. + */ + if (map.file_doesnt_need_get) + fput(map.file); vms_abort_munmap_vmas(&map.vms, &map.mas_detach); return error; } From 4c04c6b47c361612b1d70cec8f7a60b1482d1400 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 2 Apr 2026 06:44:17 -0700 Subject: [PATCH 507/700] mm/damon/stat: deallocate damon_call() failure leaking damon_ctx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit damon_stat_start() always allocates the module's damon_ctx object (damon_stat_context). Meanwhile, if damon_call() in the function fails, the damon_ctx object is not deallocated. Hence, if the damon_call() is failed, and the user writes Y to “enabled” again, the previously allocated damon_ctx object is leaked. This cannot simply be fixed by deallocating the damon_ctx object when damon_call() fails. That's because damon_call() failure doesn't guarantee the kdamond main function, which accesses the damon_ctx object, is completely finished. In other words, if damon_stat_start() deallocates the damon_ctx object after damon_call() failure, the not-yet-terminated kdamond could access the freed memory (use-after-free). Fix the leak while avoiding the use-after-free by keeping returning damon_stat_start() without deallocating the damon_ctx object after damon_call() failure, but deallocating it when the function is invoked again and the kdamond is completely terminated. If the kdamond is not yet terminated, simply return -EAGAIN, as the kdamond will soon be terminated. The issue was discovered [1] by sashiko. Link: https://lkml.kernel.org/r/20260402134418.74121-1-sj@kernel.org Link: https://lore.kernel.org/20260401012428.86694-1-sj@kernel.org [1] Fixes: 405f61996d9d ("mm/damon/stat: use damon_call() repeat mode instead of damon_callback") Signed-off-by: SeongJae Park Cc: # 6.17.x Signed-off-by: Andrew Morton --- mm/damon/stat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mm/damon/stat.c b/mm/damon/stat.c index cf2c5a541eeeaa..60351a71946063 100644 --- a/mm/damon/stat.c +++ b/mm/damon/stat.c @@ -245,6 +245,12 @@ static int damon_stat_start(void) { int err; + if (damon_stat_context) { + if (damon_is_running(damon_stat_context)) + return -EAGAIN; + damon_destroy_ctx(damon_stat_context); + } + damon_stat_context = damon_stat_build_ctx(); if (!damon_stat_context) return -ENOMEM; @@ -261,6 +267,7 @@ static void damon_stat_stop(void) { damon_stop(&damon_stat_context, 1); damon_destroy_ctx(damon_stat_context); + damon_stat_context = NULL; } static int damon_stat_enabled_store( From 7bc5da4842bed3252d26e742213741a4d0ac1b14 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Fri, 3 Apr 2026 14:38:30 +0800 Subject: [PATCH 508/700] ocfs2: fix out-of-bounds write in ocfs2_write_end_inline KASAN reports a use-after-free write of 4086 bytes in ocfs2_write_end_inline, called from ocfs2_write_end_nolock during a copy_file_range splice fallback on a corrupted ocfs2 filesystem mounted on a loop device. The actual bug is an out-of-bounds write past the inode block buffer, not a true use-after-free. The write overflows into an adjacent freed page, which KASAN reports as UAF. The root cause is that ocfs2_try_to_write_inline_data trusts the on-disk id_count field to determine whether a write fits in inline data. On a corrupted filesystem, id_count can exceed the physical maximum inline data capacity, causing writes to overflow the inode block buffer. Call trace (crash path): vfs_copy_file_range (fs/read_write.c:1634) do_splice_direct splice_direct_to_actor iter_file_splice_write ocfs2_file_write_iter generic_perform_write ocfs2_write_end ocfs2_write_end_nolock (fs/ocfs2/aops.c:1949) ocfs2_write_end_inline (fs/ocfs2/aops.c:1915) memcpy_from_folio <-- KASAN: write OOB So add id_count upper bound check in ocfs2_validate_inode_block() to alongside the existing i_size check to fix it. Link: https://lkml.kernel.org/r/20260403063830.3662739-1-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi Reported-by: syzbot+62c1793956716ea8b28a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=62c1793956716ea8b28a Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 03a51662ea8e79..a2ccd801170686 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1505,6 +1505,16 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + if (le16_to_cpu(data->id_count) > + ocfs2_max_inline_data_with_xattr(sb, di)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: inline data id_count %u exceeds max %d\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(data->id_count), + ocfs2_max_inline_data_with_xattr(sb, di)); + goto bail; + } + if (le64_to_cpu(di->i_size) > le16_to_cpu(data->id_count)) { rc = ocfs2_error(sb, "Invalid dinode #%llu: inline data i_size %llu exceeds id_count %u\n", From 591478118293c1bd628de330a99eb1eb2ef8d76b Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 19 Mar 2026 14:13:33 -0700 Subject: [PATCH 509/700] idpf: fix PREEMPT_RT raw/bh spinlock nesting for async VC handling Switch from using the completion's raw spinlock to a local lock in the idpf_vc_xn struct. The conversion is safe because complete/_all() are called outside the lock and there is no reason to share the completion lock in the current logic. This avoids invalid wait context reported by the kernel due to the async handler taking BH spinlock: [ 805.726977] ============================= [ 805.726991] [ BUG: Invalid wait context ] [ 805.727006] 7.0.0-rc2-net-devq-031026+ #28 Tainted: G S OE [ 805.727026] ----------------------------- [ 805.727038] kworker/u261:0/572 is trying to lock: [ 805.727051] ff190da6a8dbb6a0 (&vport_config->mac_filter_list_lock){+...}-{3:3}, at: idpf_mac_filter_async_handler+0xe9/0x260 [idpf] [ 805.727099] other info that might help us debug this: [ 805.727111] context-{5:5} [ 805.727119] 3 locks held by kworker/u261:0/572: [ 805.727132] #0: ff190da6db3e6148 ((wq_completion)idpf-0000:83:00.0-mbx){+.+.}-{0:0}, at: process_one_work+0x4b5/0x730 [ 805.727163] #1: ff3c6f0a6131fe50 ((work_completion)(&(&adapter->mbx_task)->work)){+.+.}-{0:0}, at: process_one_work+0x1e5/0x730 [ 805.727191] #2: ff190da765190020 (&x->wait#34){+.+.}-{2:2}, at: idpf_recv_mb_msg+0xc8/0x710 [idpf] [ 805.727218] stack backtrace: ... [ 805.727238] Workqueue: idpf-0000:83:00.0-mbx idpf_mbx_task [idpf] [ 805.727247] Call Trace: [ 805.727249] [ 805.727251] dump_stack_lvl+0x77/0xb0 [ 805.727259] __lock_acquire+0xb3b/0x2290 [ 805.727268] ? __irq_work_queue_local+0x59/0x130 [ 805.727275] lock_acquire+0xc6/0x2f0 [ 805.727277] ? idpf_mac_filter_async_handler+0xe9/0x260 [idpf] [ 805.727284] ? _printk+0x5b/0x80 [ 805.727290] _raw_spin_lock_bh+0x38/0x50 [ 805.727298] ? idpf_mac_filter_async_handler+0xe9/0x260 [idpf] [ 805.727303] idpf_mac_filter_async_handler+0xe9/0x260 [idpf] [ 805.727310] idpf_recv_mb_msg+0x1c8/0x710 [idpf] [ 805.727317] process_one_work+0x226/0x730 [ 805.727322] worker_thread+0x19e/0x340 [ 805.727325] ? __pfx_worker_thread+0x10/0x10 [ 805.727328] kthread+0xf4/0x130 [ 805.727333] ? __pfx_kthread+0x10/0x10 [ 805.727336] ret_from_fork+0x32c/0x410 [ 805.727345] ? __pfx_kthread+0x10/0x10 [ 805.727347] ret_from_fork_asm+0x1a/0x30 [ 805.727354] Fixes: 34c21fa894a1 ("idpf: implement virtchnl transaction manager") Cc: stable@vger.kernel.org Suggested-by: Sebastian Andrzej Siewior Reported-by: Ray Zhang Signed-off-by: Emil Tantilov Reviewed-by: Aleksandr Loktionov Acked-by: Sebastian Andrzej Siewior Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 14 +++++--------- drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 5 +++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index 113ecfc16dd722..582e0c8e9dc0a7 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -287,26 +287,21 @@ int idpf_send_mb_msg(struct idpf_adapter *adapter, struct idpf_ctlq_info *asq, return err; } -/* API for virtchnl "transaction" support ("xn" for short). - * - * We are reusing the completion lock to serialize the accesses to the - * transaction state for simplicity, but it could be its own separate synchro - * as well. For now, this API is only used from within a workqueue context; - * raw_spin_lock() is enough. - */ +/* API for virtchnl "transaction" support ("xn" for short). */ + /** * idpf_vc_xn_lock - Request exclusive access to vc transaction * @xn: struct idpf_vc_xn* to access */ #define idpf_vc_xn_lock(xn) \ - raw_spin_lock(&(xn)->completed.wait.lock) + spin_lock(&(xn)->lock) /** * idpf_vc_xn_unlock - Release exclusive access to vc transaction * @xn: struct idpf_vc_xn* to access */ #define idpf_vc_xn_unlock(xn) \ - raw_spin_unlock(&(xn)->completed.wait.lock) + spin_unlock(&(xn)->lock) /** * idpf_vc_xn_release_bufs - Release reference to reply buffer(s) and @@ -338,6 +333,7 @@ static void idpf_vc_xn_init(struct idpf_vc_xn_manager *vcxn_mngr) xn->state = IDPF_VC_XN_IDLE; xn->idx = i; idpf_vc_xn_release_bufs(xn); + spin_lock_init(&xn->lock); init_completion(&xn->completed); } diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h index fe065911ad5af4..6876e3ed9d1be0 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h @@ -42,8 +42,8 @@ typedef int (*async_vc_cb) (struct idpf_adapter *, struct idpf_vc_xn *, * struct idpf_vc_xn - Data structure representing virtchnl transactions * @completed: virtchnl event loop uses that to signal when a reply is * available, uses kernel completion API - * @state: virtchnl event loop stores the data below, protected by the - * completion's lock. + * @lock: protects the transaction state fields below + * @state: virtchnl event loop stores the data below, protected by @lock * @reply_sz: Original size of reply, may be > reply_buf.iov_len; it will be * truncated on its way to the receiver thread according to * reply_buf.iov_len. @@ -58,6 +58,7 @@ typedef int (*async_vc_cb) (struct idpf_adapter *, struct idpf_vc_xn *, */ struct idpf_vc_xn { struct completion completed; + spinlock_t lock; enum idpf_vc_xn_state state; size_t reply_sz; struct kvec reply; From d086fae65006368618104ba4c57779440eab2217 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 19 Mar 2026 14:13:34 -0700 Subject: [PATCH 510/700] idpf: improve locking around idpf_vc_xn_push_free() Protect the set_bit() operation for the free_xn bitmask in idpf_vc_xn_push_free(), to make the locking consistent with rest of the code and avoid potential races in that logic. Fixes: 34c21fa894a1 ("idpf: implement virtchnl transaction manager") Cc: stable@vger.kernel.org Reported-by: Ray Zhang Signed-off-by: Emil Tantilov Reviewed-by: Aleksandr Loktionov Acked-by: Sebastian Andrzej Siewior Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index 582e0c8e9dc0a7..fbd5a15b015c40 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -402,7 +402,9 @@ static void idpf_vc_xn_push_free(struct idpf_vc_xn_manager *vcxn_mngr, struct idpf_vc_xn *xn) { idpf_vc_xn_release_bufs(xn); + spin_lock_bh(&vcxn_mngr->xn_bm_lock); set_bit(xn->idx, vcxn_mngr->free_xn_bm); + spin_unlock_bh(&vcxn_mngr->xn_bm_lock); } /** From 8e2a2420e267a515f6db56a6e9570b5cacd92919 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 19 Mar 2026 14:13:35 -0700 Subject: [PATCH 511/700] idpf: set the payload size before calling the async handler Set the payload size before forwarding the reply to the async handler. Without this, xn->reply_sz will be 0 and idpf_mac_filter_async_handler() will never get past the size check. Fixes: 34c21fa894a1 ("idpf: implement virtchnl transaction manager") Cc: stable@vger.kernel.org Signed-off-by: Emil Tantilov Reviewed-by: Aleksandr Loktionov Reviewed-by: Li Li Acked-by: Sebastian Andrzej Siewior Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index fbd5a15b015c40..be66f9b2e101ca 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -615,6 +615,10 @@ idpf_vc_xn_forward_reply(struct idpf_adapter *adapter, err = -ENXIO; goto out_unlock; case IDPF_VC_XN_ASYNC: + /* Set reply_sz from the actual payload so that async_handler + * can evaluate the response. + */ + xn->reply_sz = ctlq_msg->data_len; err = idpf_vc_xn_forward_async(adapter, xn, ctlq_msg); idpf_vc_xn_unlock(xn); return err; From bb3f21edc7056cdf44a7f7bd7ba65af40741838c Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Sun, 1 Feb 2026 14:14:00 +0000 Subject: [PATCH 512/700] ice: ptp: don't WARN when controlling PF is unavailable In VFIO passthrough setups, it is possible to pass through only a PF which doesn't own the source timer. In that case the PTP controlling PF (adapter->ctrl_pf) is never initialized in the VM, so ice_get_ctrl_ptp() returns NULL and triggers WARN_ON() in ice_ptp_setup_pf(). Since this is an expected behavior in that configuration, replace WARN_ON() with an informational message and return -EOPNOTSUPP. Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead of auxdev") Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 094e96219f4565..a3c5f0a5e09b4e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -3080,7 +3080,13 @@ static int ice_ptp_setup_pf(struct ice_pf *pf) struct ice_ptp *ctrl_ptp = ice_get_ctrl_ptp(pf); struct ice_ptp *ptp = &pf->ptp; - if (WARN_ON(!ctrl_ptp) || pf->hw.mac_type == ICE_MAC_UNKNOWN) + if (!ctrl_ptp) { + dev_info(ice_pf_to_dev(pf), + "PTP unavailable: no controlling PF\n"); + return -EOPNOTSUPP; + } + + if (pf->hw.mac_type == ICE_MAC_UNKNOWN) return -ENODEV; INIT_LIST_HEAD(&ptp->port.list_node); From bf6dbadb72b997721e9b67348652926c076878d1 Mon Sep 17 00:00:00 2001 From: Petr Oros Date: Fri, 27 Mar 2026 08:46:58 +0100 Subject: [PATCH 513/700] ice: fix PTP timestamping broken by SyncE code on E825C The E825C SyncE support added in commit ad1df4f2d591 ("ice: dpll: Support E825-C SyncE and dynamic pin discovery") introduced a SyncE reconfiguration block in ice_ptp_link_change() that prevents ice_ptp_port_phy_restart() from being called in several error paths. Without the PHY restart, PTP timestamps stop working after any link change event. There are three ways the PHY restart gets blocked: 1. When DPLL initialization fails (e.g. missing ACPI firmware node properties), ICE_FLAG_DPLL is not set and the function returns early before reaching the PHY restart. 2. When ice_tspll_bypass_mux_active_e825c() fails to read the CGU register, WARN_ON_ONCE fires and the function returns early. 3. When ice_tspll_cfg_synce_ethdiv_e825c() fails to configure the clock divider for an active pin, same early return. SyncE and PTP are independent features. SyncE reconfiguration failures must not prevent the PTP PHY restart that is essential for timestamp recovery after link changes. Fix by making the entire SyncE block conditional on ICE_FLAG_DPLL without an early return, and replacing the WARN_ON_ONCE + return error handling inside the loop with dev_err_once + break. The function always proceeds to ice_ptp_port_phy_restart() regardless of SyncE errors. Fixes: ad1df4f2d591 ("ice: dpll: Support E825-C SyncE and dynamic pin discovery") Signed-off-by: Petr Oros Reviewed-by: Grzegorz Nitka Reviewed-by: Aleksandr Loktionov Tested-by: Sunitha Mekala (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ptp.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index a3c5f0a5e09b4e..6cb0cf7a98912d 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1296,12 +1296,10 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup) if (pf->hw.reset_ongoing) return; - if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) { + if (hw->mac_type == ICE_MAC_GENERIC_3K_E825 && + test_bit(ICE_FLAG_DPLL, pf->flags)) { int pin, err; - if (!test_bit(ICE_FLAG_DPLL, pf->flags)) - return; - mutex_lock(&pf->dplls.lock); for (pin = 0; pin < ICE_SYNCE_CLK_NUM; pin++) { enum ice_synce_clk clk_pin; @@ -1314,15 +1312,19 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup) port_num, &active, clk_pin); - if (WARN_ON_ONCE(err)) { - mutex_unlock(&pf->dplls.lock); - return; + if (err) { + dev_err_once(ice_pf_to_dev(pf), + "Failed to read SyncE bypass mux for pin %d, err %d\n", + pin, err); + break; } err = ice_tspll_cfg_synce_ethdiv_e825c(hw, clk_pin); - if (active && WARN_ON_ONCE(err)) { - mutex_unlock(&pf->dplls.lock); - return; + if (active && err) { + dev_err_once(ice_pf_to_dev(pf), + "Failed to configure SyncE ETH divider for pin %d, err %d\n", + pin, err); + break; } } mutex_unlock(&pf->dplls.lock); From d8ae40dc20cbd7bb6e6b36a928e2db2296060ad2 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Wed, 4 Mar 2026 09:42:32 +0100 Subject: [PATCH 514/700] ixgbe: stop re-reading flash on every get_drvinfo for e610 ixgbe_get_drvinfo() calls ixgbe_refresh_fw_version() on every ethtool query for e610 adapters. That ends up in ixgbe_discover_flash_size(), which bisects the full 16 MB NVM space issuing one ACI command per step (~20 ms each, ~24 steps total = ~500 ms). Profiling on an idle E610-XAT2 system with telegraf scraping ethtool stats every 10 seconds: kretprobe:ixgbe_get_drvinfo took 527603 us kretprobe:ixgbe_get_drvinfo took 523978 us kretprobe:ixgbe_get_drvinfo took 552975 us kretprobe:ice_get_drvinfo took 3 us kretprobe:igb_get_drvinfo took 2 us kretprobe:i40e_get_drvinfo took 5 us The half-second stall happens under the RTNL lock, causing visible latency on ip-link and friends. The FW version can only change after an EMPR reset. All flash data is already populated at probe time and the cached adapter->eeprom_id is what get_drvinfo should be returning. The only place that needs to trigger a re-read is ixgbe_devlink_reload_empr_finish(), right after the EMPR completes and new firmware is running. Additionally, refresh the FW version in ixgbe_reinit_locked() so that any PF that undergoes a reinit after an EMPR (e.g. triggered by another PF's devlink reload) also picks up the new version in adapter->eeprom_id. ixgbe_devlink_info_get() keeps its refresh call for explicit "devlink dev info" queries, which is fine given those are user-initiated. Fixes: c9e563cae19e ("ixgbe: add support for devlink reload") Co-developed-by: Jedrzej Jagielski Signed-off-by: Jedrzej Jagielski Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ixgbe/devlink/devlink.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 13 +++++++------ drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 10 ++++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/devlink/devlink.c b/drivers/net/ethernet/intel/ixgbe/devlink/devlink.c index b0404f37271a81..cf8908b82f8a33 100644 --- a/drivers/net/ethernet/intel/ixgbe/devlink/devlink.c +++ b/drivers/net/ethernet/intel/ixgbe/devlink/devlink.c @@ -474,7 +474,7 @@ static int ixgbe_devlink_reload_empr_finish(struct devlink *devlink, adapter->flags2 &= ~(IXGBE_FLAG2_API_MISMATCH | IXGBE_FLAG2_FW_ROLLBACK); - return 0; + return ixgbe_refresh_fw_version(adapter); } static const struct devlink_ops ixgbe_devlink_ops = { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index dce4936708eb44..047f04045585a1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -973,7 +973,7 @@ int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter); bool ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, u16 subdevice_id); void ixgbe_set_fw_version_e610(struct ixgbe_adapter *adapter); -void ixgbe_refresh_fw_version(struct ixgbe_adapter *adapter); +int ixgbe_refresh_fw_version(struct ixgbe_adapter *adapter); #ifdef CONFIG_PCI_IOV void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter); #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 56aabaa5caecac..ba049b3a9609e8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1155,12 +1155,17 @@ static int ixgbe_set_eeprom(struct net_device *netdev, return ret_val; } -void ixgbe_refresh_fw_version(struct ixgbe_adapter *adapter) +int ixgbe_refresh_fw_version(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; + int err; + + err = ixgbe_get_flash_data(hw); + if (err) + return err; - ixgbe_get_flash_data(hw); ixgbe_set_fw_version_e610(adapter); + return 0; } static void ixgbe_get_drvinfo(struct net_device *netdev, @@ -1168,10 +1173,6 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, { struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); - /* need to refresh info for e610 in case fw reloads in runtime */ - if (adapter->hw.mac.type == ixgbe_mac_e610) - ixgbe_refresh_fw_version(adapter); - strscpy(drvinfo->driver, ixgbe_driver_name, sizeof(drvinfo->driver)); strscpy(drvinfo->fw_version, adapter->eeprom_id, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index e4101c59074d68..59801187a83925 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6289,6 +6289,16 @@ void ixgbe_reinit_locked(struct ixgbe_adapter *adapter) if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) msleep(2000); ixgbe_up(adapter); + + /* E610 has no FW event to notify all PFs of an EMPR reset, so + * refresh the FW version here to pick up any new FW version after + * a hardware reset (e.g. EMPR triggered by another PF's devlink + * reload). ixgbe_refresh_fw_version() updates both hw->flash and + * adapter->eeprom_id so ethtool -i reports the correct string. + */ + if (adapter->hw.mac.type == ixgbe_mac_e610) + (void)ixgbe_refresh_fw_version(adapter); + clear_bit(__IXGBE_RESETTING, &adapter->state); } From 4821d563cd7f251ae728be1a6d04af82a294a5b9 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 13 Mar 2026 09:22:29 +0100 Subject: [PATCH 515/700] ixgbevf: add missing negotiate_features op to Hyper-V ops table Commit a7075f501bd3 ("ixgbevf: fix mailbox API compatibility by negotiating supported features") added the .negotiate_features callback to ixgbe_mac_operations and populated it in ixgbevf_mac_ops, but forgot to add it to ixgbevf_hv_mac_ops. This leaves the function pointer NULL on Hyper-V VMs. During probe, ixgbevf_negotiate_api() calls ixgbevf_set_features(), which unconditionally dereferences hw->mac.ops.negotiate_features(). On Hyper-V this results in a NULL pointer dereference: BUG: kernel NULL pointer dereference, address: 0000000000000000 [...] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine [...] Workqueue: events work_for_cpu_fn RIP: 0010:0x0 [...] Call Trace: ixgbevf_negotiate_api+0x66/0x160 [ixgbevf] ixgbevf_sw_init+0xe4/0x1f0 [ixgbevf] ixgbevf_probe+0x20f/0x4a0 [ixgbevf] local_pci_probe+0x50/0xa0 work_for_cpu_fn+0x1a/0x30 [...] Add ixgbevf_hv_negotiate_features_vf() that returns -EOPNOTSUPP and wire it into ixgbevf_hv_mac_ops. The caller already handles -EOPNOTSUPP gracefully. Fixes: a7075f501bd3 ("ixgbevf: fix mailbox API compatibility by negotiating supported features") Reported-by: Xiaoqiang Xiong Closes: https://issues.redhat.com/browse/RHEL-155455 Assisted-by: Claude:claude-4.6-opus-high Cursor Tested-by: Xiaoqiang Xiong Signed-off-by: Michal Schmidt Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ixgbevf/vf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index b67b580f7f1c95..f6df86d124b9e8 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -709,6 +709,12 @@ static int ixgbevf_negotiate_features_vf(struct ixgbe_hw *hw, u32 *pf_features) return err; } +static int ixgbevf_hv_negotiate_features_vf(struct ixgbe_hw *hw, + u32 *pf_features) +{ + return -EOPNOTSUPP; +} + /** * ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address * @hw: pointer to the HW structure @@ -1142,6 +1148,7 @@ static const struct ixgbe_mac_operations ixgbevf_hv_mac_ops = { .setup_link = ixgbevf_setup_mac_link_vf, .check_link = ixgbevf_hv_check_mac_link_vf, .negotiate_api_version = ixgbevf_hv_negotiate_api_version_vf, + .negotiate_features = ixgbevf_hv_negotiate_features_vf, .set_rar = ixgbevf_hv_set_rar_vf, .update_mc_addr_list = ixgbevf_hv_update_mc_addr_list_vf, .update_xcast_mode = ixgbevf_hv_update_xcast_mode, From b1e067240379f950a0022208e0685f3465c211cb Mon Sep 17 00:00:00 2001 From: Alex Dvoretsky Date: Thu, 12 Mar 2026 14:52:55 +0100 Subject: [PATCH 516/700] igb: remove napi_synchronize() in igb_down() When an AF_XDP zero-copy application terminates abruptly (e.g., kill -9), the XSK buffer pool is destroyed but NAPI polling continues. igb_clean_rx_irq_zc() repeatedly returns the full budget, preventing napi_complete_done() from clearing NAPI_STATE_SCHED. igb_down() calls napi_synchronize() before napi_disable() for each queue vector. napi_synchronize() spins waiting for NAPI_STATE_SCHED to clear, which never happens. igb_down() blocks indefinitely, the TX watchdog fires, and the TX queue remains permanently stalled. napi_disable() already handles this correctly: it sets NAPI_STATE_DISABLE. After a full-budget poll, __napi_poll() checks napi_disable_pending(). If set, it forces completion and clears NAPI_STATE_SCHED, breaking the loop that napi_synchronize() cannot. napi_synchronize() was added in commit 41f149a285da ("igb: Fix possible panic caused by Rx traffic arrival while interface is down"). napi_disable() provides stronger guarantees: it prevents further scheduling and waits for any active poll to exit. Other Intel drivers (ixgbe, ice, i40e) use napi_disable() without a preceding napi_synchronize() in their down paths. Remove redundant napi_synchronize() call and reorder napi_disable() before igb_set_queue_napi() so the queue-to-NAPI mapping is only cleared after polling has fully stopped. Fixes: 2c6196013f84 ("igb: Add AF_XDP zero-copy Rx support") Cc: stable@vger.kernel.org Suggested-by: Maciej Fijalkowski Reviewed-by: Aleksandr Loktionov Signed-off-by: Alex Dvoretsky Reviewed-by: Maciej Fijalkowski Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igb/igb_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ee99fd8fd51378..ce91dda00ec0e9 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2203,9 +2203,8 @@ void igb_down(struct igb_adapter *adapter) for (i = 0; i < adapter->num_q_vectors; i++) { if (adapter->q_vector[i]) { - napi_synchronize(&adapter->q_vector[i]->napi); - igb_set_queue_napi(adapter, i, NULL); napi_disable(&adapter->q_vector[i]->napi); + igb_set_queue_napi(adapter, i, NULL); } } From d3baa34a470771399c1495bc04b1e26ac15d598e Mon Sep 17 00:00:00 2001 From: Agalakov Daniil Date: Wed, 18 Mar 2026 15:05:05 +0300 Subject: [PATCH 517/700] e1000: check return value of e1000_read_eeprom [Why] e1000_set_eeprom() performs a read-modify-write operation when the write range is not word-aligned. This requires reading the first and last words of the range from the EEPROM to preserve the unmodified bytes. However, the code does not check the return value of e1000_read_eeprom(). If the read fails, the operation continues using uninitialized data from eeprom_buff. This results in corrupted data being written back to the EEPROM for the boundary words. Add the missing error checks and abort the operation if reading fails. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Co-developed-by: Iskhakov Daniil Signed-off-by: Iskhakov Daniil Signed-off-by: Agalakov Daniil Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000/e1000_ethtool.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index ab232b3fbbd0e4..4dcbeabb3ad25e 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -496,14 +496,19 @@ static int e1000_set_eeprom(struct net_device *netdev, */ ret_val = e1000_read_eeprom(hw, first_word, 1, &eeprom_buff[0]); + if (ret_val) + goto out; + ptr++; } - if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) { + if ((eeprom->offset + eeprom->len) & 1) { /* need read/modify/write of last changed EEPROM word * only the first byte of the word is being modified */ ret_val = e1000_read_eeprom(hw, last_word, 1, &eeprom_buff[last_word - first_word]); + if (ret_val) + goto out; } /* Device's eeprom is always little-endian, word addressable */ @@ -522,6 +527,7 @@ static int e1000_set_eeprom(struct net_device *netdev, if ((ret_val == 0) && (first_word <= EEPROM_CHECKSUM_REG)) e1000_update_eeprom_checksum(hw); +out: kfree(eeprom_buff); return ret_val; } From a315e022a72d95ef5f1d4e58e903cb492b0ad931 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:51 +0200 Subject: [PATCH 518/700] xsk: tighten UMEM headroom validation to account for tailroom and min frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current headroom validation in xdp_umem_reg() could leave us with insufficient space dedicated to even receive minimum-sized ethernet frame. Furthermore if multi-buffer would come to play then skb_shared_info stored at the end of XSK frame would be corrupted. HW typically works with 128-aligned sizes so let us provide this value as bare minimum. Multi-buffer setting is known later in the configuration process so besides accounting for 128 bytes, let us also take care of tailroom space upfront. Reviewed-by: Björn Töpel Acked-by: Stanislav Fomichev Fixes: 99e3a236dd43 ("xsk: Add missing check on user supplied headroom size") Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-2-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- net/xdp/xdp_umem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c index 066ce07c506d51..58da2f4f4397aa 100644 --- a/net/xdp/xdp_umem.c +++ b/net/xdp/xdp_umem.c @@ -203,7 +203,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr) if (!unaligned_chunks && chunks_rem) return -EINVAL; - if (headroom >= chunk_size - XDP_PACKET_HEADROOM) + if (headroom > chunk_size - XDP_PACKET_HEADROOM - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) - 128) return -EINVAL; if (mr->flags & XDP_UMEM_TX_METADATA_LEN) { From 1ee1605138fc94cc8f8f273321dd2471c64977f9 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:52 +0200 Subject: [PATCH 519/700] xsk: respect tailroom for ZC setups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multi-buffer XDP stores information about frags in skb_shared_info that sits at the tailroom of a packet. The storage space is reserved via xdp_data_hard_end(): ((xdp)->data_hard_start + (xdp)->frame_sz - \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) and then we refer to it via macro below: static inline struct skb_shared_info * xdp_get_shared_info_from_buff(const struct xdp_buff *xdp) { return (struct skb_shared_info *)xdp_data_hard_end(xdp); } Currently we do not respect this tailroom space in multi-buffer AF_XDP ZC scenario. To address this, introduce xsk_pool_get_tailroom() and use it within xsk_pool_get_rx_frame_size() which is used in ZC drivers to configure length of HW Rx buffer. Typically drivers on Rx Hw buffers side work on 128 byte alignment so let us align the value returned by xsk_pool_get_rx_frame_size() in order to avoid addressing this on driver's side. This addresses the fact that idpf uses mentioned function *before* pool->dev being set so we were at risk that after subtracting tailroom we would not provide 128-byte aligned value to HW. Since xsk_pool_get_rx_frame_size() is actively used in xsk_rcv_check() and __xsk_rcv(), add a variant of this routine that will not include 128 byte alignment and therefore old behavior is preserved. Reviewed-by: Björn Töpel Acked-by: Stanislav Fomichev Fixes: 24ea50127ecf ("xsk: support mbuf on ZC RX") Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-3-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- include/net/xdp_sock_drv.h | 23 ++++++++++++++++++++++- net/xdp/xsk.c | 4 ++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 6b9ebae2dc952e..46797645a0c241 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -41,16 +41,37 @@ static inline u32 xsk_pool_get_headroom(struct xsk_buff_pool *pool) return XDP_PACKET_HEADROOM + pool->headroom; } +static inline u32 xsk_pool_get_tailroom(bool mbuf) +{ + return mbuf ? SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) : 0; +} + static inline u32 xsk_pool_get_chunk_size(struct xsk_buff_pool *pool) { return pool->chunk_size; } -static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool) +static inline u32 __xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool) { return xsk_pool_get_chunk_size(pool) - xsk_pool_get_headroom(pool); } +static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool) +{ + u32 frame_size = __xsk_pool_get_rx_frame_size(pool); + struct xdp_umem *umem = pool->umem; + bool mbuf; + + /* Reserve tailroom only for zero-copy pools that opted into + * multi-buffer. The reserved area is used for skb_shared_info, + * matching the XDP core's xdp_data_hard_end() layout. + */ + mbuf = pool->dev && (umem->flags & XDP_UMEM_SG_FLAG); + frame_size -= xsk_pool_get_tailroom(mbuf); + + return ALIGN_DOWN(frame_size, 128); +} + static inline u32 xsk_pool_get_rx_frag_step(struct xsk_buff_pool *pool) { return pool->unaligned ? 0 : xsk_pool_get_chunk_size(pool); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 6149f6a798971f..c8ef9e427c9cd5 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -239,7 +239,7 @@ static u32 xsk_copy_xdp(void *to, void **from, u32 to_len, static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) { - u32 frame_size = xsk_pool_get_rx_frame_size(xs->pool); + u32 frame_size = __xsk_pool_get_rx_frame_size(xs->pool); void *copy_from = xsk_copy_xdp_start(xdp), *copy_to; u32 from_len, meta_len, rem, num_desc; struct xdp_buff_xsk *xskb; @@ -338,7 +338,7 @@ static int xsk_rcv_check(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index) return -EINVAL; - if (len > xsk_pool_get_rx_frame_size(xs->pool) && !xs->sg) { + if (len > __xsk_pool_get_rx_frame_size(xs->pool) && !xs->sg) { xs->rx_dropped++; return -ENOSPC; } From 93e84fe45b752d17a5a46b306ed78f0133bbc719 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:53 +0200 Subject: [PATCH 520/700] xsk: fix XDP_UMEM_SG_FLAG issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently xp_assign_dev_shared() is missing XDP_USE_SG being propagated to flags so set it in order to preserve mtu check that is supposed to be done only when no multi-buffer setup is in picture. Also, this flag has the same value as XDP_UMEM_TX_SW_CSUM so we could get unexpected SG setups for software Tx checksums. Since csum flag is UAPI, modify value of XDP_UMEM_SG_FLAG. Fixes: d609f3d228a8 ("xsk: add multi-buffer support for sockets sharing umem") Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-4-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 2 +- net/xdp/xsk_buff_pool.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 23e8861e8b25e6..ebac60a3d8a17b 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -14,7 +14,7 @@ #include #include -#define XDP_UMEM_SG_FLAG (1 << 1) +#define XDP_UMEM_SG_FLAG BIT(3) struct net_device; struct xsk_queue; diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 37b7a68b89b3d3..729602a3cec0e8 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -247,6 +247,10 @@ int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs, struct xdp_umem *umem = umem_xs->umem; flags = umem->zc ? XDP_ZEROCOPY : XDP_COPY; + + if (umem->flags & XDP_UMEM_SG_FLAG) + flags |= XDP_USE_SG; + if (umem_xs->pool->uses_need_wakeup) flags |= XDP_USE_NEED_WAKEUP; From 36ee60b569ba0dfb6f961333b90d19ab5b323fa9 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:54 +0200 Subject: [PATCH 521/700] xsk: validate MTU against usable frame size on bind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AF_XDP bind currently accepts zero-copy pool configurations without verifying that the device MTU fits into the usable frame space provided by the UMEM chunk. This becomes a problem since we started to respect tailroom which is subtracted from chunk_size (among with headroom). 2k chunk size might not provide enough space for standard 1500 MTU, so let us catch such settings at bind time. Furthermore, validate whether underlying HW will be able to satisfy configured MTU wrt XSK's frame size multiplied by supported Rx buffer chain length (that is exposed via net_device::xdp_zc_max_segs). Fixes: 24ea50127ecf ("xsk: support mbuf on ZC RX") Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-5-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- net/xdp/xsk_buff_pool.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 729602a3cec0e8..cd7bc50872f6b5 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -10,6 +10,8 @@ #include "xdp_umem.h" #include "xsk.h" +#define ETH_PAD_LEN (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN) + void xp_add_xsk(struct xsk_buff_pool *pool, struct xdp_sock *xs) { if (!xs->tx) @@ -157,8 +159,12 @@ static void xp_disable_drv_zc(struct xsk_buff_pool *pool) int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *netdev, u16 queue_id, u16 flags) { + u32 needed = netdev->mtu + ETH_PAD_LEN; + u32 segs = netdev->xdp_zc_max_segs; + bool mbuf = flags & XDP_USE_SG; bool force_zc, force_copy; struct netdev_bpf bpf; + u32 frame_size; int err = 0; ASSERT_RTNL(); @@ -178,7 +184,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool, if (err) return err; - if (flags & XDP_USE_SG) + if (mbuf) pool->umem->flags |= XDP_UMEM_SG_FLAG; if (flags & XDP_USE_NEED_WAKEUP) @@ -200,8 +206,24 @@ int xp_assign_dev(struct xsk_buff_pool *pool, goto err_unreg_pool; } - if (netdev->xdp_zc_max_segs == 1 && (flags & XDP_USE_SG)) { - err = -EOPNOTSUPP; + if (mbuf) { + if (segs == 1) { + err = -EOPNOTSUPP; + goto err_unreg_pool; + } + } else { + segs = 1; + } + + /* open-code xsk_pool_get_rx_frame_size() as pool->dev is not + * set yet at this point; we are before getting down to driver + */ + frame_size = __xsk_pool_get_rx_frame_size(pool) - + xsk_pool_get_tailroom(mbuf); + frame_size = ALIGN_DOWN(frame_size, 128); + + if (needed > frame_size * segs) { + err = -EINVAL; goto err_unreg_pool; } From c5866a6be47207b05c6eead6292c8551c8a91669 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:55 +0200 Subject: [PATCH 522/700] selftests: bpf: introduce a common routine for reading procfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parametrize current way of getting MAX_SKB_FRAGS value from {sys,proc}fs so that it can be re-used to get cache line size of system's CPU. All that just to mimic and compute size of kernel's struct skb_shared_info which for xsk and test suite interpret as tailroom. Introduce two variables to ifobject struct that will carry count of skb frags and tailroom size. Do the reading and computing once, at the beginning of test suite execution in xskxceiver, but for test_progs such way is not possible as in this environment each test setups and torns down ifobject structs. Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-6-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- .../selftests/bpf/prog_tests/test_xsk.c | 25 +------------------ .../selftests/bpf/prog_tests/test_xsk.h | 23 +++++++++++++++++ tools/testing/selftests/bpf/prog_tests/xsk.c | 19 ++++++++++++++ tools/testing/selftests/bpf/xskxceiver.c | 23 +++++++++++++++++ 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index 7e38ec6e656b97..62118ffba661f0 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -179,25 +179,6 @@ int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem return xsk_socket__create(&xsk->xsk, ifobject->ifindex, 0, umem->umem, rxr, txr, &cfg); } -#define MAX_SKB_FRAGS_PATH "/proc/sys/net/core/max_skb_frags" -static unsigned int get_max_skb_frags(void) -{ - unsigned int max_skb_frags = 0; - FILE *file; - - file = fopen(MAX_SKB_FRAGS_PATH, "r"); - if (!file) { - ksft_print_msg("Error opening %s\n", MAX_SKB_FRAGS_PATH); - return 0; - } - - if (fscanf(file, "%u", &max_skb_frags) != 1) - ksft_print_msg("Error reading %s\n", MAX_SKB_FRAGS_PATH); - - fclose(file); - return max_skb_frags; -} - static int set_ring_size(struct ifobject *ifobj) { int ret; @@ -2242,11 +2223,7 @@ int testapp_too_many_frags(struct test_spec *test) if (test->mode == TEST_MODE_ZC) { max_frags = test->ifobj_tx->xdp_zc_max_segs; } else { - max_frags = get_max_skb_frags(); - if (!max_frags) { - ksft_print_msg("Can't get MAX_SKB_FRAGS from system, using default (17)\n"); - max_frags = 17; - } + max_frags = test->ifobj_tx->max_skb_frags; max_frags += 1; } diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.h b/tools/testing/selftests/bpf/prog_tests/test_xsk.h index 8fc78a057de0be..1ab8aee4ce5640 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.h +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.h @@ -31,6 +31,9 @@ #define SOCK_RECONF_CTR 10 #define USLEEP_MAX 10000 +#define MAX_SKB_FRAGS_PATH "/proc/sys/net/core/max_skb_frags" +#define SMP_CACHE_BYTES_PATH "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size" + extern bool opt_verbose; #define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0) @@ -45,6 +48,24 @@ static inline u64 ceil_u64(u64 a, u64 b) return (a + b - 1) / b; } +static inline unsigned int read_procfs_val(const char *path) +{ + unsigned int read_val = 0; + FILE *file; + + file = fopen(path, "r"); + if (!file) { + ksft_print_msg("Error opening %s\n", path); + return 0; + } + + if (fscanf(file, "%u", &read_val) != 1) + ksft_print_msg("Error reading %s\n", path); + + fclose(file); + return read_val; +} + /* Simple test */ enum test_mode { TEST_MODE_SKB, @@ -115,6 +136,8 @@ struct ifobject { int mtu; u32 bind_flags; u32 xdp_zc_max_segs; + u32 umem_tailroom; + u32 max_skb_frags; bool tx_on; bool rx_on; bool use_poll; diff --git a/tools/testing/selftests/bpf/prog_tests/xsk.c b/tools/testing/selftests/bpf/prog_tests/xsk.c index dd4c35c0e428b4..6e2f63ee2a6c37 100644 --- a/tools/testing/selftests/bpf/prog_tests/xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/xsk.c @@ -62,6 +62,7 @@ int configure_ifobj(struct ifobject *tx, struct ifobject *rx) static void test_xsk(const struct test_spec *test_to_run, enum test_mode mode) { + u32 max_frags, umem_tailroom, cache_line_size; struct ifobject *ifobj_tx, *ifobj_rx; struct test_spec test; int ret; @@ -84,6 +85,24 @@ static void test_xsk(const struct test_spec *test_to_run, enum test_mode mode) ifobj_tx->set_ring.default_rx = ifobj_tx->ring.rx_pending; } + cache_line_size = read_procfs_val(SMP_CACHE_BYTES_PATH); + if (!cache_line_size) + cache_line_size = 64; + + max_frags = read_procfs_val(MAX_SKB_FRAGS_PATH); + if (!max_frags) + max_frags = 17; + + ifobj_tx->max_skb_frags = max_frags; + ifobj_rx->max_skb_frags = max_frags; + + /* 48 bytes is a part of skb_shared_info w/o frags array; + * 16 bytes is sizeof(skb_frag_t) + */ + umem_tailroom = ALIGN(48 + (max_frags * 16), cache_line_size); + ifobj_tx->umem_tailroom = umem_tailroom; + ifobj_rx->umem_tailroom = umem_tailroom; + if (!ASSERT_OK(init_iface(ifobj_rx, worker_testapp_validate_rx), "init RX")) goto delete_rx; if (!ASSERT_OK(init_iface(ifobj_tx, worker_testapp_validate_tx), "init TX")) diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c index 05b3cebc5ca93f..7dad8556a722e4 100644 --- a/tools/testing/selftests/bpf/xskxceiver.c +++ b/tools/testing/selftests/bpf/xskxceiver.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -333,6 +334,7 @@ static void print_tests(void) int main(int argc, char **argv) { const size_t total_tests = ARRAY_SIZE(tests) + ARRAY_SIZE(ci_skip_tests); + u32 cache_line_size, max_frags, umem_tailroom; struct pkt_stream *rx_pkt_stream_default; struct pkt_stream *tx_pkt_stream_default; struct ifobject *ifobj_tx, *ifobj_rx; @@ -354,6 +356,27 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); + cache_line_size = read_procfs_val(SMP_CACHE_BYTES_PATH); + if (!cache_line_size) { + ksft_print_msg("Can't get SMP_CACHE_BYTES from system, using default (64)\n"); + cache_line_size = 64; + } + + max_frags = read_procfs_val(MAX_SKB_FRAGS_PATH); + if (!max_frags) { + ksft_print_msg("Can't get MAX_SKB_FRAGS from system, using default (17)\n"); + max_frags = 17; + } + ifobj_tx->max_skb_frags = max_frags; + ifobj_rx->max_skb_frags = max_frags; + + /* 48 bytes is a part of skb_shared_info w/o frags array; + * 16 bytes is sizeof(skb_frag_t) + */ + umem_tailroom = ALIGN(48 + (max_frags * 16), cache_line_size); + ifobj_tx->umem_tailroom = umem_tailroom; + ifobj_rx->umem_tailroom = umem_tailroom; + parse_command_line(ifobj_tx, ifobj_rx, argc, argv); if (opt_print_tests) { From 3197c51ce2fa00410f86f4829e4f9223553632f6 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:56 +0200 Subject: [PATCH 523/700] selftests: bpf: fix pkt grow tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip tail adjust tests in xskxceiver for SKB mode as it is not very friendly for it. multi-buffer case does not work as xdp_rxq_info that is registered for generic XDP does not report ::frag_size. The non-mbuf path copies packet via skb_pp_cow_data() which only accounts for headroom, leaving us with no tailroom and causing underlying XDP prog to drop packets therefore. For multi-buffer test on other modes, change the amount of bytes we use for growth, assume worst-case scenario and take care of headroom and tailroom. Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-7-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- .../selftests/bpf/prog_tests/test_xsk.c | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index 62118ffba661f0..ee60bcc22ee4f3 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -2528,16 +2528,34 @@ int testapp_adjust_tail_shrink_mb(struct test_spec *test) int testapp_adjust_tail_grow(struct test_spec *test) { + if (test->mode == TEST_MODE_SKB) + return TEST_SKIP; + /* Grow by 4 bytes for testing purpose */ return testapp_adjust_tail(test, 4, MIN_PKT_SIZE * 2); } int testapp_adjust_tail_grow_mb(struct test_spec *test) { + u32 grow_size; + + if (test->mode == TEST_MODE_SKB) + return TEST_SKIP; + + /* worst case scenario is when underlying setup will work on 3k + * buffers, let us account for it; given that we will use 6k as + * pkt_len, expect that it will be broken down to 2 descs each + * with 3k payload; + * + * 4k is truesize, 3k payload, 256 HR, 320 TR; + */ + grow_size = XSK_UMEM__MAX_FRAME_SIZE - + XSK_UMEM__LARGE_FRAME_SIZE - + XDP_PACKET_HEADROOM - + test->ifobj_tx->umem_tailroom; test->mtu = MAX_ETH_JUMBO_SIZE; - /* Grow by (frag_size - last_frag_Size) - 1 to stay inside the last fragment */ - return testapp_adjust_tail(test, (XSK_UMEM__MAX_FRAME_SIZE / 2) - 1, - XSK_UMEM__LARGE_FRAME_SIZE * 2); + + return testapp_adjust_tail(test, grow_size, XSK_UMEM__LARGE_FRAME_SIZE * 2); } int testapp_tx_queue_consumer(struct test_spec *test) From 16546954e117e65661109e788682891b15d4e3ce Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:57 +0200 Subject: [PATCH 524/700] selftests: bpf: have a separate variable for drop test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently two different XDP programs share a static variable for different purposes (picking where to redirect on shared umem test & whether to drop a packet). This can be a problem when running full test suite - idx can be written by shared umem test and this value can cause a false behavior within XDP drop half test. Introduce a dedicated variable for drop half test so that these two don't step on each other toes. There is no real need for using __sync_fetch_and_add here as XSK tests are executed on single CPU. Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-8-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/bpf/progs/xsk_xdp_progs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c b/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c index 683306db8594e1..023d8befd4cab0 100644 --- a/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c +++ b/tools/testing/selftests/bpf/progs/xsk_xdp_progs.c @@ -26,8 +26,10 @@ SEC("xdp.frags") int xsk_def_prog(struct xdp_md *xdp) SEC("xdp.frags") int xsk_xdp_drop(struct xdp_md *xdp) { + static unsigned int drop_idx; + /* Drop every other packet */ - if (idx++ % 2) + if (drop_idx++ % 2) return XDP_DROP; return bpf_redirect_map(&xsk, 0, XDP_DROP); From 62838e363e4f0753d43b2b78e9d3f6ad3c9102ec Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Thu, 2 Apr 2026 17:49:58 +0200 Subject: [PATCH 525/700] selftests: bpf: adjust rx_dropped xskxceiver's test to respect tailroom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we have changed how big user defined headroom in umem can be, change the logic in testapp_stats_rx_dropped() so we pass updated headroom validation in xdp_umem_reg() and still drop half of frames. Test works on non-mbuf setup so __xsk_pool_get_rx_frame_size() that is called on xsk_rcv_check() will not account skb_shared_info size. Taking the tailroom size into account in test being fixed is needed as xdp_umem_reg() defaults to respect it. Reviewed-by: Björn Töpel Signed-off-by: Maciej Fijalkowski Link: https://patch.msgid.link/20260402154958.562179-9-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/bpf/prog_tests/test_xsk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index ee60bcc22ee4f3..7950c504ed2899 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -1959,15 +1959,17 @@ int testapp_headroom(struct test_spec *test) int testapp_stats_rx_dropped(struct test_spec *test) { + u32 umem_tr = test->ifobj_tx->umem_tailroom; + if (test->mode == TEST_MODE_ZC) { ksft_print_msg("Can not run RX_DROPPED test for ZC mode\n"); return TEST_SKIP; } - if (pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0)) + if (pkt_stream_replace_half(test, (MIN_PKT_SIZE * 3) + umem_tr, 0)) return TEST_FAILURE; test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size - - XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 3; + XDP_PACKET_HEADROOM - (MIN_PKT_SIZE * 2) - umem_tr; if (pkt_stream_receive_half(test)) return TEST_FAILURE; test->ifobj_rx->validation_func = validate_rx_dropped; From 24ad7ff668896325591fa0b570f2cca6c55f136f Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 3 Apr 2026 11:32:51 +0200 Subject: [PATCH 526/700] vsock/test: fix send_buf()/recv_buf() EINTR handling When send() or recv() returns -1 with errno == EINTR, the code skips the break but still adds the return value to nwritten/nread, making it decrease by 1. This leads to wrong buffer offsets and wrong bytes count. Fix it by explicitly continuing the loop on EINTR, so the return value is only added when it is positive. Fixes: a8ed71a27ef5 ("vsock/test: add recv_buf() utility function") Fixes: 12329bd51fdc ("vsock/test: add send_buf() utility function") Signed-off-by: Stefano Garzarella Reviewed-by: Luigi Leonardi Link: https://patch.msgid.link/20260403093251.30662-1-sgarzare@redhat.com Signed-off-by: Jakub Kicinski --- tools/testing/vsock/util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 9430ef5b8bc3ea..1fe1338c79cd15 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -344,7 +344,9 @@ void send_buf(int fd, const void *buf, size_t len, int flags, ret = send(fd, buf + nwritten, len - nwritten, flags); timeout_check("send"); - if (ret == 0 || (ret < 0 && errno != EINTR)) + if (ret < 0 && errno == EINTR) + continue; + if (ret <= 0) break; nwritten += ret; @@ -396,7 +398,9 @@ void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) ret = recv(fd, buf + nread, len - nread, flags); timeout_check("recv"); - if (ret == 0 || (ret < 0 && errno != EINTR)) + if (ret < 0 && errno == EINTR) + continue; + if (ret <= 0) break; nread += ret; From 0f42e3f4fe2a58394e37241d02d9ca6ab7b7d516 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Fri, 3 Apr 2026 09:45:12 +0800 Subject: [PATCH 527/700] net: skb: fix cross-cache free of KFENCE-allocated skb head SKB_SMALL_HEAD_CACHE_SIZE is intentionally set to a non-power-of-2 value (e.g. 704 on x86_64) to avoid collisions with generic kmalloc bucket sizes. This ensures that skb_kfree_head() can reliably use skb_end_offset to distinguish skb heads allocated from skb_small_head_cache vs. generic kmalloc caches. However, when KFENCE is enabled, kfence_ksize() returns the exact requested allocation size instead of the slab bucket size. If a caller (e.g. bpf_test_init) allocates skb head data via kzalloc() and the requested size happens to equal SKB_SMALL_HEAD_CACHE_SIZE, then slab_build_skb() -> ksize() returns that exact value. After subtracting skb_shared_info overhead, skb_end_offset ends up matching SKB_SMALL_HEAD_HEADROOM, causing skb_kfree_head() to incorrectly free the object to skb_small_head_cache instead of back to the original kmalloc cache, resulting in a slab cross-cache free: kmem_cache_free(skbuff_small_head): Wrong slab cache. Expected skbuff_small_head but got kmalloc-1k Fix this by always calling kfree(head) in skb_kfree_head(). This keeps the free path generic and avoids allocator-specific misclassification for KFENCE objects. Fixes: bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head") Reported-by: Antonius Closes: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/ Signed-off-by: Jiayuan Chen Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260403014517.142550-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/core/skbuff.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0e217041958a83..43ee86dcf2eaf3 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1083,10 +1083,7 @@ static int skb_pp_frag_ref(struct sk_buff *skb) static void skb_kfree_head(void *head, unsigned int end_offset) { - if (end_offset == SKB_SMALL_HEAD_HEADROOM) - kmem_cache_free(net_hotdata.skb_small_head_cache, head); - else - kfree(head); + kfree(head); } static void skb_free_head(struct sk_buff *skb) From a9d4f4f6e65e0bf9bbddedecc84d67249991979c Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Fri, 3 Apr 2026 12:17:56 +0300 Subject: [PATCH 528/700] net/mlx5: Update the list of the PCI supported devices Add the upcoming ConnectX-10 NVLink-C2C device ID to the table of supported PCI device IDs. Cc: stable@vger.kernel.org Signed-off-by: Michael Guralnik Reviewed-by: Patrisious Haddad Signed-off-by: Tariq Toukan Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260403091756.139583-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index fdc3ba20912e4f..3f73d9b1115dd1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -2267,6 +2267,7 @@ static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1023) }, /* ConnectX-8 */ { PCI_VDEVICE(MELLANOX, 0x1025) }, /* ConnectX-9 */ { PCI_VDEVICE(MELLANOX, 0x1027) }, /* ConnectX-10 */ + { PCI_VDEVICE(MELLANOX, 0x2101) }, /* ConnectX-10 NVLink-C2C */ { PCI_VDEVICE(MELLANOX, 0xa2d2) }, /* BlueField integrated ConnectX-5 network controller */ { PCI_VDEVICE(MELLANOX, 0xa2d3), MLX5_PCI_DEV_IS_VF}, /* BlueField integrated ConnectX-5 network controller VF */ { PCI_VDEVICE(MELLANOX, 0xa2d6) }, /* BlueField-2 integrated ConnectX-6 Dx network controller */ From e2a39d1a88f00ed83ebc7a19b7673d4ffd50b173 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Fri, 13 Mar 2026 10:40:47 -0700 Subject: [PATCH 529/700] perf/x86/intel/uncore: Fix iounmap() leak on global_init failure Kernel test robot reported: Unverified Error/Warning (likely false positive, kindly check if interested): arch/x86/events/intel/uncore_discovery.c:293:2-8: ERROR: missing iounmap; ioremap on line 288 and execution via conditional on line 292 If domain->global_init() fails in __parse_discovery_table(), the ioremap'ed MMIO region is not released before returning, resulting in an MMIO mapping leak. Fixes: b575fc0e3357 ("perf/x86/intel/uncore: Add domain global init callback") Reported-by: kernel test robot Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Link: https://patch.msgid.link/20260313174050.171704-2-zide.chen@intel.com --- arch/x86/events/intel/uncore_discovery.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c index 12e259a988dd17..427939be2fb400 100644 --- a/arch/x86/events/intel/uncore_discovery.c +++ b/arch/x86/events/intel/uncore_discovery.c @@ -264,6 +264,7 @@ static int __parse_discovery_table(struct uncore_discovery_domain *domain, struct uncore_unit_discovery unit; void __iomem *io_addr; unsigned long size; + int ret = 0; int i; size = UNCORE_DISCOVERY_GLOBAL_MAP_SIZE; @@ -273,21 +274,23 @@ static int __parse_discovery_table(struct uncore_discovery_domain *domain, /* Read Global Discovery State */ memcpy_fromio(&global, io_addr, sizeof(struct uncore_global_discovery)); + iounmap(io_addr); + if (uncore_discovery_invalid_unit(global)) { pr_info("Invalid Global Discovery State: 0x%llx 0x%llx 0x%llx\n", global.table1, global.ctl, global.table3); - iounmap(io_addr); return -EINVAL; } - iounmap(io_addr); size = (1 + global.max_units) * global.stride * 8; io_addr = ioremap(addr, size); if (!io_addr) return -ENOMEM; - if (domain->global_init && domain->global_init(global.ctl)) - return -ENODEV; + if (domain->global_init && domain->global_init(global.ctl)) { + ret = -ENODEV; + goto out; + } /* Parsing Unit Discovery State */ for (i = 0; i < global.max_units; i++) { @@ -307,8 +310,10 @@ static int __parse_discovery_table(struct uncore_discovery_domain *domain, } *parsed = true; + +out: iounmap(io_addr); - return 0; + return ret; } static int parse_discovery_table(struct uncore_discovery_domain *domain, From 7b568e9eba2fad89a696f22f0413d44cf4a1f892 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Fri, 13 Mar 2026 10:40:48 -0700 Subject: [PATCH 530/700] perf/x86/intel/uncore: Skip discovery table for offline dies This warning can be triggered if NUMA is disabled and the system boots with fewer CPUs than the number of CPUs in die 0. WARNING: CPU: 9 PID: 7257 at uncore.c:1157 uncore_pci_pmu_register+0x136/0x160 [intel_uncore] Currently, the discovery table continues to be parsed even if all CPUs in the associated die are offline. This can lead to an array overflow at "pmu->boxes[die] = box" in uncore_pci_pmu_register(), which may trigger the warning above or cause other issues. Fixes: edae1f06c2cd ("perf/x86/intel/uncore: Parse uncore discovery tables") Reported-by: Steve Wahl Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Tested-by: Steve Wahl Link: https://patch.msgid.link/20260313174050.171704-3-zide.chen@intel.com --- arch/x86/events/intel/uncore_discovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c index 427939be2fb400..583cbd06b9b881 100644 --- a/arch/x86/events/intel/uncore_discovery.c +++ b/arch/x86/events/intel/uncore_discovery.c @@ -371,7 +371,7 @@ static bool uncore_discovery_pci(struct uncore_discovery_domain *domain) (val & UNCORE_DISCOVERY_DVSEC2_BIR_MASK) * UNCORE_DISCOVERY_BIR_STEP; die = get_device_die_id(dev); - if (die < 0) + if ((die < 0) || (die >= uncore_max_dies())) continue; parse_discovery_table(domain, dev, die, bar_offset, &parsed); From a16d1ec4dd0cdcf689f324adde6067083bce9099 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Fri, 13 Mar 2026 10:40:49 -0700 Subject: [PATCH 531/700] perf/x86/intel/uncore: Fix die ID init and look up bugs In snbep_pci2phy_map_init(), in the nr_node_ids > 8 path, uncore_device_to_die() may return -1 when all CPUs associated with the UBOX device are offline. Remove the WARN_ON_ONCE(die_id == -1) check for two reasons: - The current code breaks out of the loop. This is incorrect because pci_get_device() does not guarantee iteration in domain or bus order, so additional UBOX devices may be skipped during the scan. - Returning -EINVAL is incorrect, since marking offline buses with die_id == -1 is expected and should not be treated as an error. Separately, when NUMA is disabled on a NUMA-capable platform, pcibus_to_node() returns NUMA_NO_NODE, causing uncore_device_to_die() to return -1 for all PCI devices. As a result, spr_update_device_location(), used on Intel SPR and EMR, ignores the corresponding PMON units and does not add them to the RB tree. Fix this by using uncore_pcibus_to_dieid(), which retrieves topology from the UBOX GIDNIDMAP register and works regardless of whether NUMA is enabled in Linux. This requires snbep_pci2phy_map_init() to be added in spr_uncore_pci_init(). Keep uncore_device_to_die() only for the nr_node_ids > 8 case, where NUMA is expected to be enabled. Fixes: 9a7832ce3d92 ("perf/x86/intel/uncore: With > 8 nodes, get pci bus die id from NUMA info") Fixes: 65248a9a9ee1 ("perf/x86/uncore: Add a quirk for UPI on SPR") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Tested-by: Steve Wahl Link: https://patch.msgid.link/20260313174050.171704-4-zide.chen@intel.com --- arch/x86/events/intel/uncore.c | 1 + arch/x86/events/intel/uncore_snbep.c | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c index 786bd51a0d89c4..e9cc1ba921c5ed 100644 --- a/arch/x86/events/intel/uncore.c +++ b/arch/x86/events/intel/uncore.c @@ -67,6 +67,7 @@ int uncore_die_to_segment(int die) return bus ? pci_domain_nr(bus) : -EINVAL; } +/* Note: This API can only be used when NUMA information is available. */ int uncore_device_to_die(struct pci_dev *dev) { int node = pcibus_to_node(dev->bus); diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index 0a1d08136cc108..a4b337bf83c0b1 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -1459,13 +1459,7 @@ static int snbep_pci2phy_map_init(int devid, int nodeid_loc, int idmap_loc, bool } map->pbus_to_dieid[bus] = die_id = uncore_device_to_die(ubox_dev); - raw_spin_unlock(&pci2phy_map_lock); - - if (WARN_ON_ONCE(die_id == -1)) { - err = -EINVAL; - break; - } } } @@ -6420,7 +6414,7 @@ static void spr_update_device_location(int type_id) while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, device, dev)) != NULL) { - die = uncore_device_to_die(dev); + die = uncore_pcibus_to_dieid(dev->bus); if (die < 0) continue; @@ -6444,6 +6438,11 @@ static void spr_update_device_location(int type_id) int spr_uncore_pci_init(void) { + int ret = snbep_pci2phy_map_init(0x3250, SKX_CPUNODEID, SKX_GIDNIDMAP, true); + + if (ret) + return ret; + /* * The discovery table of UPI on some SPR variant is broken, * which impacts the detection of both UPI and M3UPI uncore PMON. From 16bcbe6738bea7b4aee0a29324ce12c21c4b0ea0 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Fri, 13 Mar 2026 10:40:50 -0700 Subject: [PATCH 532/700] perf/x86/intel/uncore: Remove extra double quote mark The third argument in INTEL_UNCORE_FR_EVENT_DESC() is subject to __stringify(), and the extra double quote marks can result in the expansion "3.814697266e-6" in the sysfs knobs, instead of 3.814697266e-6. This is incorrect, though it may still work for perf, e.g. perf stat -e uncore_iio_free_running_0/bw_in_port0/ Fixes: d8987048f665 ("perf/x86/intel/uncore: Support IIO free-running counters on DMR") Closes: https://lore.kernel.org/all/20251231224233.113839-1-zide.chen@intel.com/ Reported-by: Chun-Tse Shao Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Chun-Tse Shao Reviewed-by: Dapeng Mi Link: https://patch.msgid.link/20260313174050.171704-5-zide.chen@intel.com --- arch/x86/events/intel/uncore_snbep.c | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index a4b337bf83c0b1..215d33e260ede7 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -6934,34 +6934,34 @@ static struct freerunning_counters dmr_iio_freerunning[] = { static struct uncore_event_desc dmr_uncore_iio_freerunning_events[] = { /* ITC Free Running Data BW counter for inbound traffic */ - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port0, 0x10, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port1, 0x11, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port2, 0x12, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port3, 0x13, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port4, 0x14, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port5, 0x15, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port6, 0x16, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(inb_data_port7, 0x17, "3.814697266e-6"), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port0, 0x10, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port1, 0x11, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port2, 0x12, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port3, 0x13, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port4, 0x14, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port5, 0x15, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port6, 0x16, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(inb_data_port7, 0x17, 3.814697266e-6), /* ITC Free Running BW IN counters */ - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, "3.814697266e-6"), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, 3.814697266e-6), /* ITC Free Running BW OUT counters */ - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x30, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x31, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x32, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x33, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port4, 0x34, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port5, 0x35, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port6, 0x36, "3.814697266e-6"), - INTEL_UNCORE_FR_EVENT_DESC(bw_out_port7, 0x37, "3.814697266e-6"), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x30, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x31, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x32, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x33, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port4, 0x34, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port5, 0x35, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port6, 0x36, 3.814697266e-6), + INTEL_UNCORE_FR_EVENT_DESC(bw_out_port7, 0x37, 3.814697266e-6), /* Free Running Clock Counter */ INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x40"), From 105c42566a550e2d05fc14f763216a8765ee5d0e Mon Sep 17 00:00:00 2001 From: Arthur Husband Date: Mon, 6 Apr 2026 15:23:35 -0700 Subject: [PATCH 533/700] ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585 The JMicron JMB585 (and JMB582) SATA controllers advertise 64-bit DMA support via the S64A bit in the AHCI CAP register, but their 64-bit DMA implementation is defective. Under sustained I/O, DMA transfers targeting addresses above 4GB silently corrupt data -- writes land at incorrect memory addresses with no errors logged. The failure pattern is similar to the ASMedia ASM1061 (commit 20730e9b2778 ("ahci: add 43-bit DMA address quirk for ASMedia ASM1061 controllers")), which also falsely advertised full 64-bit DMA support. However, the JMB585 requires a stricter 32-bit DMA mask rather than 43-bit, as corruption occurs with any address above 4GB. On the Minisforum N5 Pro specifically, the combination of the JMB585's broken 64-bit DMA with the AMD Family 1Ah (Strix Point) IOMMU causes silent data corruption that is only detectable via checksumming filesystems (BTRFS/ZFS scrub). The corruption occurs when 32-bit IOVA space is exhausted and the kernel transparently switches to 64-bit DMA addresses. Add device-specific PCI ID entries for the JMB582 (0x0582) and JMB585 (0x0585) before the generic JMicron class match, using a new board type that combines AHCI_HFLAG_IGN_IRQ_IF_ERR (preserving existing behavior) with AHCI_HFLAG_32BIT_ONLY to force 32-bit DMA masks. Signed-off-by: Arthur Husband Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel --- drivers/ata/ahci.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 931d0081169b9c..1d73a53370cf3e 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -68,6 +68,7 @@ enum board_ids { /* board IDs for specific chipsets in alphabetical order */ board_ahci_al, board_ahci_avn, + board_ahci_jmb585, board_ahci_mcp65, board_ahci_mcp77, board_ahci_mcp89, @@ -212,6 +213,15 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_avn_ops, }, + /* JMicron JMB582/585: 64-bit DMA is broken, force 32-bit */ + [board_ahci_jmb585] = { + AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR | + AHCI_HFLAG_32BIT_ONLY), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, [board_ahci_mcp65] = { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), @@ -439,6 +449,10 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Elkhart Lake IDs 0x4b60 & 0x4b62 https://sata-io.org/product/8803 not tested yet */ { PCI_VDEVICE(INTEL, 0x4b63), board_ahci_pcs_quirk }, /* Elkhart Lake AHCI */ + /* JMicron JMB582/585: force 32-bit DMA (broken 64-bit implementation) */ + { PCI_VDEVICE(JMICRON, 0x0582), board_ahci_jmb585 }, + { PCI_VDEVICE(JMICRON, 0x0585), board_ahci_jmb585 }, + /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, From 75519f5df2a9b23f7bf305e12dc9a6e3e65c24b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 27 Mar 2026 13:45:53 +0200 Subject: [PATCH 534/700] drm/i915/psr: Do not use pipe_src as borders for SU area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This far using crtc_state->pipe_src as borders for Selective Update area haven't caused visible problems as drm_rect_width(crtc_state->pipe_src) == crtc_state->hw.adjusted_mode.crtc_hdisplay and drm_rect_height(crtc_state->pipe_src) == crtc_state->hw.adjusted_mode.crtc_vdisplay when pipe scaling is not used. On the other hand using pipe scaling is forcing full frame updates and all the Selective Update area calculations are skipped. Now this improper usage of crtc_state->pipe_src is causing following warnings: <4> [7771.978166] xe 0000:00:02.0: [drm] drm_WARN_ON_ONCE(su_lines % vdsc_cfg->slice_height) after WARN_ON_ONCE was added by commit: "drm/i915/dsc: Add helper for writing DSC Selective Update ET parameters" These warnings are seen when DSC and pipe scaling are enabled simultaneously. This is because on full frame update SU area is improperly set as pipe_src which is not aligned with DSC slice height. Fix these by creating local rectangle using crtc_state->hw.adjusted_mode.crtc_hdisplay and crtc_state->hw.adjusted_mode.crtc_vdisplay. Use this local rectangle as borders for SU area. Fixes: d6774b8c3c58 ("drm/i915: Ensure damage clip area is within pipe area") Cc: # v6.0+ Signed-off-by: Jouni Högander Reviewed-by: Mika Kahola Link: https://patch.msgid.link/20260327114553.195285-1-jouni.hogander@intel.com (cherry picked from commit da0cdc1c329dd2ff09c41fbbe9fbd9c92c5d2c6e) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_psr.c | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 3791944389db0c..097e18c1adb279 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2678,9 +2678,9 @@ static u32 psr2_pipe_srcsz_early_tpt_calc(struct intel_crtc_state *crtc_state, static void clip_area_update(struct drm_rect *overlap_damage_area, struct drm_rect *damage_area, - struct drm_rect *pipe_src) + struct drm_rect *display_area) { - if (!drm_rect_intersect(damage_area, pipe_src)) + if (!drm_rect_intersect(damage_area, display_area)) return; if (overlap_damage_area->y1 == -1) { @@ -2731,6 +2731,7 @@ static bool intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_st static void intel_psr2_sel_fetch_et_alignment(struct intel_atomic_state *state, struct intel_crtc *crtc, + struct drm_rect *display_area, bool *cursor_in_su_area) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); @@ -2758,7 +2759,7 @@ intel_psr2_sel_fetch_et_alignment(struct intel_atomic_state *state, continue; clip_area_update(&crtc_state->psr2_su_area, &new_plane_state->uapi.dst, - &crtc_state->pipe_src); + display_area); *cursor_in_su_area = true; } } @@ -2855,6 +2856,12 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_plane_state *new_plane_state, *old_plane_state; struct intel_plane *plane; + struct drm_rect display_area = { + .x1 = 0, + .y1 = 0, + .x2 = crtc_state->hw.adjusted_mode.crtc_hdisplay, + .y2 = crtc_state->hw.adjusted_mode.crtc_vdisplay, + }; bool full_update = false, su_area_changed; int i, ret; @@ -2868,7 +2875,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, crtc_state->psr2_su_area.x1 = 0; crtc_state->psr2_su_area.y1 = -1; - crtc_state->psr2_su_area.x2 = drm_rect_width(&crtc_state->pipe_src); + crtc_state->psr2_su_area.x2 = drm_rect_width(&display_area); crtc_state->psr2_su_area.y2 = -1; /* @@ -2906,14 +2913,14 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.y1 = old_plane_state->uapi.dst.y1; damaged_area.y2 = old_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); } if (new_plane_state->uapi.visible) { damaged_area.y1 = new_plane_state->uapi.dst.y1; damaged_area.y2 = new_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); } continue; } else if (new_plane_state->uapi.alpha != old_plane_state->uapi.alpha) { @@ -2921,7 +2928,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.y1 = new_plane_state->uapi.dst.y1; damaged_area.y2 = new_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); continue; } @@ -2937,7 +2944,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.x1 += new_plane_state->uapi.dst.x1 - src.x1; damaged_area.x2 += new_plane_state->uapi.dst.x1 - src.x1; - clip_area_update(&crtc_state->psr2_su_area, &damaged_area, &crtc_state->pipe_src); + clip_area_update(&crtc_state->psr2_su_area, &damaged_area, &display_area); } /* @@ -2972,7 +2979,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, * cursor is added into affected planes even when * cursor is not updated by itself. */ - intel_psr2_sel_fetch_et_alignment(state, crtc, &cursor_in_su_area); + intel_psr2_sel_fetch_et_alignment(state, crtc, &display_area, + &cursor_in_su_area); su_area_changed = intel_psr2_sel_fetch_pipe_alignment(crtc_state); @@ -3048,8 +3056,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, skip_sel_fetch_set_loop: if (full_update) - clip_area_update(&crtc_state->psr2_su_area, &crtc_state->pipe_src, - &crtc_state->pipe_src); + clip_area_update(&crtc_state->psr2_su_area, &display_area, + &display_area); psr2_man_trk_ctl_calc(crtc_state, full_update); crtc_state->pipe_srcsz_early_tpt = From 069daad4f2ae9c5c108131995529d5f02392c446 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 2 Apr 2026 13:31:04 +0200 Subject: [PATCH 535/700] xfrm: Wait for RCU readers during policy netns exit xfrm_policy_fini() frees the policy_bydst hash tables after flushing the policy work items and deleting all policies, but it does not wait for concurrent RCU readers to leave their read-side critical sections first. The policy_bydst tables are published via rcu_assign_pointer() and are looked up through rcu_dereference_check(), so netns teardown must also wait for an RCU grace period before freeing the table memory. Fix this by adding synchronize_rcu() before freeing the policy hash tables. Fixes: e1e551bc5630 ("xfrm: policy: prepare policy_bydst hash for rcu lookups") Signed-off-by: Steffen Klassert Reviewed-by: Florian Westphal --- net/xfrm/xfrm_policy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 362939aa56cfb7..8f0188e763c778 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4290,6 +4290,8 @@ static void xfrm_policy_fini(struct net *net) #endif xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false); + synchronize_rcu(); + WARN_ON(!list_empty(&net->xfrm.policy_all)); for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { From 1c428b03840094410c5fb6a5db30640486bbbfcb Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Thu, 2 Apr 2026 19:44:01 +0800 Subject: [PATCH 536/700] xfrm: hold dev ref until after transport_finish NF_HOOK After async crypto completes, xfrm_input_resume() calls dev_put() immediately on re-entry before the skb reaches transport_finish. The skb->dev pointer is then used inside NF_HOOK and its okfn, which can race with device teardown. Remove the dev_put from the async resumption entry and instead drop the reference after the NF_HOOK call in transport_finish, using a saved device pointer since NF_HOOK may consume the skb. This covers NF_DROP, NF_QUEUE and NF_STOLEN paths that skip the okfn. For non-transport exits (decaps, gro, drop) and secondary async return points, release the reference inline when async is set. Suggested-by: Florian Westphal Fixes: acf568ee859f ("xfrm: Reinject transport-mode packets through tasklet") Cc: stable@vger.kernel.org Signed-off-by: Qi Tang Signed-off-by: Steffen Klassert --- net/ipv4/xfrm4_input.c | 5 ++++- net/ipv6/xfrm6_input.c | 5 ++++- net/xfrm/xfrm_input.c | 18 ++++++++++++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index f28cfd88eaf593..c2eac844bcdb4e 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -50,6 +50,7 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) { struct xfrm_offload *xo = xfrm_offload(skb); struct iphdr *iph = ip_hdr(skb); + struct net_device *dev = skb->dev; iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol; @@ -73,8 +74,10 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) } NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, - dev_net(skb->dev), NULL, skb, skb->dev, NULL, + dev_net(dev), NULL, skb, dev, NULL, xfrm4_rcv_encap_finish); + if (async) + dev_put(dev); return 0; } diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 9005fc156a20e6..699a001ac16629 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -43,6 +43,7 @@ static int xfrm6_transport_finish2(struct net *net, struct sock *sk, int xfrm6_transport_finish(struct sk_buff *skb, int async) { struct xfrm_offload *xo = xfrm_offload(skb); + struct net_device *dev = skb->dev; int nhlen = -skb_network_offset(skb); skb_network_header(skb)[IP6CB(skb)->nhoff] = @@ -68,8 +69,10 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) } NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, - dev_net(skb->dev), NULL, skb, skb->dev, NULL, + dev_net(dev), NULL, skb, dev, NULL, xfrm6_transport_finish2); + if (async) + dev_put(dev); return 0; } diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index dc1312ed5a0955..f65291eba1f68c 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -506,7 +506,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) /* An encap_type of -1 indicates async resumption. */ if (encap_type == -1) { async = 1; - dev_put(skb->dev); seq = XFRM_SKB_CB(skb)->seq.input.low; spin_lock(&x->lock); goto resume; @@ -659,8 +658,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) dev_hold(skb->dev); nexthdr = x->type->input(x, skb); - if (nexthdr == -EINPROGRESS) + if (nexthdr == -EINPROGRESS) { + if (async) + dev_put(skb->dev); return 0; + } dev_put(skb->dev); spin_lock(&x->lock); @@ -695,9 +697,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) XFRM_MODE_SKB_CB(skb)->protocol = nexthdr; err = xfrm_inner_mode_input(x, skb); - if (err == -EINPROGRESS) + if (err == -EINPROGRESS) { + if (async) + dev_put(skb->dev); return 0; - else if (err) { + } else if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); goto drop; } @@ -734,6 +738,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) sp->olen = 0; if (skb_valid_dst(skb)) skb_dst_drop(skb); + if (async) + dev_put(skb->dev); gro_cells_receive(&gro_cells, skb); return 0; } else { @@ -753,6 +759,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) sp->olen = 0; if (skb_valid_dst(skb)) skb_dst_drop(skb); + if (async) + dev_put(skb->dev); gro_cells_receive(&gro_cells, skb); return err; } @@ -763,6 +771,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) drop_unlock: spin_unlock(&x->lock); drop: + if (async) + dev_put(skb->dev); xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; From 83317cce60a032c49480dcdabe146435bd689d03 Mon Sep 17 00:00:00 2001 From: Kotlyarov Mihail Date: Sat, 4 Apr 2026 12:05:20 +0300 Subject: [PATCH 537/700] xfrm: fix refcount leak in xfrm_migrate_policy_find syzkaller reported a memory leak in xfrm_policy_alloc: BUG: memory leak unreferenced object 0xffff888114d79000 (size 1024): comm "syz.1.17", pid 931 ... xfrm_policy_alloc+0xb3/0x4b0 net/xfrm/xfrm_policy.c:432 The root cause is a double call to xfrm_pol_hold_rcu() in xfrm_migrate_policy_find(). The lookup function already returns a policy with held reference, making the second call redundant. Remove the redundant xfrm_pol_hold_rcu() call to fix the refcount imbalance and prevent the memory leak. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 563d5ca93e88 ("xfrm: switch migrate to xfrm_policy_lookup_bytype") Signed-off-by: Kotlyarov Mihail Reviewed-by: Florian Westphal Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 8f0188e763c778..a872af5610dc95 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4528,9 +4528,6 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector * pol = xfrm_policy_lookup_bytype(net, type, &fl, sel->family, dir, if_id); if (IS_ERR_OR_NULL(pol)) goto out_unlock; - - if (!xfrm_pol_hold_rcu(pol)) - pol = NULL; out_unlock: rcu_read_unlock(); return pol; From 1beb76b2053b68c491b78370794b8ff63c8f8c02 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Apr 2026 17:33:03 +0200 Subject: [PATCH 538/700] xfrm_user: fix info leak in build_mapping() struct xfrm_usersa_id has a one-byte padding hole after the proto field, which ends up never getting set to zero before copying out to userspace. Fix that up by zeroing out the whole structure before setting individual variables. Fixes: 3a2dfbe8acb1 ("xfrm: Notify changes in UDP encapsulation via netlink") Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: Simon Horman Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index a779590c985a54..baa43c325da201 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -4172,6 +4172,7 @@ static int build_mapping(struct sk_buff *skb, struct xfrm_state *x, um = nlmsg_data(nlh); + memset(&um->id, 0, sizeof(um->id)); memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr)); um->id.spi = x->id.spi; um->id.family = x->props.family; From d10119968d0e1f2b669604baf2a8b5fdb72fa6b4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Apr 2026 17:34:22 +0200 Subject: [PATCH 539/700] xfrm_user: fix info leak in build_report() struct xfrm_user_report is a __u8 proto field followed by a struct xfrm_selector which means there is three "empty" bytes of padding, but the padding is never zeroed before copying to userspace. Fix that up by zeroing the structure before setting individual member variables. Cc: stable Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: Simon Horman Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index baa43c325da201..d56450f6166912 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -4125,6 +4125,7 @@ static int build_report(struct sk_buff *skb, u8 proto, return -EMSGSIZE; ur = nlmsg_data(nlh); + memset(ur, 0, sizeof(*ur)); ur->proto = proto; memcpy(&ur->sel, sel, sizeof(ur->sel)); From 426c355742f02cf743b347d9d7dbdc1bfbfa31ef Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Sun, 22 Mar 2026 11:46:08 -0700 Subject: [PATCH 540/700] net: af_key: zero aligned sockaddr tail in PF_KEY exports PF_KEY export paths use `pfkey_sockaddr_size()` when reserving sockaddr payload space, so IPv6 addresses occupy 32 bytes on the wire. However, `pfkey_sockaddr_fill()` initializes only the first 28 bytes of `struct sockaddr_in6`, leaving the final 4 aligned bytes uninitialized. Not every PF_KEY message is affected. The state and policy dump builders already zero the whole message buffer before filling the sockaddr payloads. Keep the fix to the export paths that still append aligned sockaddr payloads with plain `skb_put()`: - `SADB_ACQUIRE` - `SADB_X_NAT_T_NEW_MAPPING` - `SADB_X_MIGRATE` Fix those paths by clearing only the aligned sockaddr tail after `pfkey_sockaddr_fill()`. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 08de61beab8a ("[PFKEYV2]: Extension for dynamic update of endpoint address(es)") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Xiao Liu Signed-off-by: Zhengchuan Liang Signed-off-by: Steffen Klassert --- net/key/af_key.c | 52 +++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/net/key/af_key.c b/net/key/af_key.c index 72ac2ace419de4..5d480ae394058d 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -757,6 +757,22 @@ static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port return 0; } +static unsigned int pfkey_sockaddr_fill_zero_tail(const xfrm_address_t *xaddr, + __be16 port, + struct sockaddr *sa, + unsigned short family) +{ + unsigned int prefixlen; + int sockaddr_len = pfkey_sockaddr_len(family); + int sockaddr_size = pfkey_sockaddr_size(family); + + prefixlen = pfkey_sockaddr_fill(xaddr, port, sa, family); + if (sockaddr_size > sockaddr_len) + memset((u8 *)sa + sockaddr_len, 0, sockaddr_size - sockaddr_len); + + return prefixlen; +} + static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, int add_keys, int hsc) { @@ -3206,9 +3222,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; addr->sadb_address_prefixlen = - pfkey_sockaddr_fill(&x->props.saddr, 0, - (struct sockaddr *) (addr + 1), - x->props.family); + pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0, + (struct sockaddr *)(addr + 1), + x->props.family); if (!addr->sadb_address_prefixlen) BUG(); @@ -3221,9 +3237,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; addr->sadb_address_prefixlen = - pfkey_sockaddr_fill(&x->id.daddr, 0, - (struct sockaddr *) (addr + 1), - x->props.family); + pfkey_sockaddr_fill_zero_tail(&x->id.daddr, 0, + (struct sockaddr *)(addr + 1), + x->props.family); if (!addr->sadb_address_prefixlen) BUG(); @@ -3421,9 +3437,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; addr->sadb_address_prefixlen = - pfkey_sockaddr_fill(&x->props.saddr, 0, - (struct sockaddr *) (addr + 1), - x->props.family); + pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0, + (struct sockaddr *)(addr + 1), + x->props.family); if (!addr->sadb_address_prefixlen) BUG(); @@ -3443,9 +3459,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; addr->sadb_address_prefixlen = - pfkey_sockaddr_fill(ipaddr, 0, - (struct sockaddr *) (addr + 1), - x->props.family); + pfkey_sockaddr_fill_zero_tail(ipaddr, 0, + (struct sockaddr *)(addr + 1), + x->props.family); if (!addr->sadb_address_prefixlen) BUG(); @@ -3474,15 +3490,15 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type, switch (type) { case SADB_EXT_ADDRESS_SRC: addr->sadb_address_prefixlen = sel->prefixlen_s; - pfkey_sockaddr_fill(&sel->saddr, 0, - (struct sockaddr *)(addr + 1), - sel->family); + pfkey_sockaddr_fill_zero_tail(&sel->saddr, 0, + (struct sockaddr *)(addr + 1), + sel->family); break; case SADB_EXT_ADDRESS_DST: addr->sadb_address_prefixlen = sel->prefixlen_d; - pfkey_sockaddr_fill(&sel->daddr, 0, - (struct sockaddr *)(addr + 1), - sel->family); + pfkey_sockaddr_fill_zero_tail(&sel->daddr, 0, + (struct sockaddr *)(addr + 1), + sel->family); break; default: return -EINVAL; From db5b8cecbdf479ad13156af750377e5b43853fab Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Mon, 30 Mar 2026 18:19:14 +0200 Subject: [PATCH 541/700] pinctrl: mcp23s08: Disable all pin interrupts during probe A chip being probed may have the interrupt-on-change feature enabled on some of its pins, for example after a reboot. This can cause the chip to generate interrupts for pins that don't have a registered nested handler, which leads to a kernel crash such as below: [ 7.928897] Unable to handle kernel read from unreadable memory at virtual address 00000000000000ac [ 7.932314] Mem abort info: [ 7.935081] ESR = 0x0000000096000004 [ 7.938808] EC = 0x25: DABT (current EL), IL = 32 bits [ 7.944094] SET = 0, FnV = 0 [ 7.947127] EA = 0, S1PTW = 0 [ 7.950247] FSC = 0x04: level 0 translation fault [ 7.955101] Data abort info: [ 7.957961] ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 [ 7.963421] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 7.968447] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 7.973734] user pgtable: 4k pages, 48-bit VAs, pgdp=00000000089b7000 [ 7.980148] [00000000000000ac] pgd=0000000000000000, p4d=0000000000000000 [ 7.986913] Internal error: Oops: 0000000096000004 [#1] SMP [ 7.992545] Modules linked in: [ 8.073678] CPU: 0 UID: 0 PID: 81 Comm: irq/18-4-0025 Not tainted 7.0.0-rc6-gd2b5a1f931c8-dirty #199 [ 8.073689] Hardware name: Khadas VIM3 (DT) [ 8.073692] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 8.094639] pc : _raw_spin_lock_irq+0x40/0x80 [ 8.098970] lr : handle_nested_irq+0x2c/0x168 [ 8.098979] sp : ffff800082b2bd20 [ 8.106599] x29: ffff800082b2bd20 x28: ffff800080107920 x27: ffff800080104d88 [ 8.106611] x26: ffff000003298080 x25: 0000000000000001 x24: 000000000000ff00 [ 8.113707] x23: 0000000000000001 x22: 0000000000000000 x21: 000000000000000e [ 8.120850] x20: 0000000000000000 x19: 00000000000000ac x18: 0000000000000000 [ 8.135046] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 [ 8.135062] x14: ffff800081567ea8 x13: ffffffffffffffff x12: 0000000000000000 [ 8.135070] x11: 00000000000000c0 x10: 0000000000000b60 x9 : ffff800080109e0c [ 8.135078] x8 : 1fffe0000069dbc1 x7 : 0000000000000001 x6 : ffff0000034ede00 [ 8.135086] x5 : 0000000000000000 x4 : ffff0000034ede08 x3 : 0000000000000001 [ 8.163460] x2 : 0000000000000000 x1 : 0000000000000001 x0 : 00000000000000ac [ 8.170560] Call trace: [ 8.180094] _raw_spin_lock_irq+0x40/0x80 (P) [ 8.184443] mcp23s08_irq+0x248/0x358 [ 8.184462] irq_thread_fn+0x34/0xb8 [ 8.184470] irq_thread+0x1a4/0x310 [ 8.195093] kthread+0x13c/0x150 [ 8.198309] ret_from_fork+0x10/0x20 [ 8.201850] Code: d65f03c0 d2800002 52800023 f9800011 (885ffc01) [ 8.207931] ---[ end trace 0000000000000000 ]--- This issue has always been present, but has been latent until commit "f9f4fda15e72" ("pinctrl: mcp23s08: init reg_defaults from HW at probe and switch cache type"), which correctly removed reg_defaults from the regmap and as a side effect changed the behavior of the interrupt handler so that the real value of the MCP_GPINTEN register is now being read from the chip instead of using a bogus 0 default value; a non-zero value for this register can trigger the invocation of a nested handler which may not exist (yet). Fix this issue by disabling all pin interrupts during initialization. Fixes: f9f4fda15e72 ("pinctrl: mcp23s08: init reg_defaults from HW at probe and switch cache type") Signed-off-by: Francesco Lavra Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-mcp23s08.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 586f2f67c6177f..b89b3169e8be55 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -664,6 +664,15 @@ int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, if (mcp->irq && mcp->irq_controller) { struct gpio_irq_chip *girq = &mcp->chip.irq; + /* + * Disable all pin interrupts, to prevent the interrupt handler from + * calling nested handlers for any currently-enabled interrupts that + * do not (yet) have an actual handler. + */ + ret = mcp_write(mcp, MCP_GPINTEN, 0); + if (ret < 0) + return dev_err_probe(dev, ret, "can't disable interrupts\n"); + gpio_irq_chip_set_chip(girq, &mcp23s08_irq_chip); /* This will let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; From 12cd7632757a54ce586e36040210b1a738a0fc53 Mon Sep 17 00:00:00 2001 From: Thomas Fourier Date: Wed, 18 Feb 2026 14:07:37 +0100 Subject: [PATCH 542/700] wifi: brcmsmac: Fix dma_free_coherent() size dma_alloc_consistent() may change the size to align it. The new size is saved in alloced. Change the free size to match the allocation size. Fixes: 5b435de0d786 ("net: wireless: add brcm80211 drivers") Cc: Signed-off-by: Thomas Fourier Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260218130741.46566-3-fourier.thomas@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c index 9f6ef7ce1b582d..a329c20e92fbcf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c @@ -483,7 +483,7 @@ static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size, if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr & boundary)) { *alignbits = dma_align_sizetobits(size); - dma_free_coherent(di->dmadev, size, va, *descpa); + dma_free_coherent(di->dmadev, *alloced, va, *descpa); va = dma_alloc_consistent(di, size, *alignbits, alloced, descpa); } From 304950a467d83678bd0b0f46331882e2ac23b12d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 23 Mar 2026 15:45:51 +0800 Subject: [PATCH 543/700] wifi: brcmfmac: validate bsscfg indices in IF events brcmf_fweh_handle_if_event() validates the firmware-provided interface index before it touches drvr->iflist[], but it still uses the raw bsscfgidx field as an array index without a matching range check. Reject IF events whose bsscfg index does not fit in drvr->iflist[] before indexing the interface array. Signed-off-by: Pengpeng Hou Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260323074551.93530-1-pengpeng@iscas.ac.cn [add missing wifi prefix] Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 984886481f4e05..1cff4ba7694303 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -153,6 +153,11 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx); return; } + if (ifevent->bsscfgidx >= BRCMF_MAX_IFS) { + bphy_err(drvr, "invalid bsscfg index: %u\n", + ifevent->bsscfgidx); + return; + } ifp = drvr->iflist[ifevent->bsscfgidx]; From 25369b22223d1c56e42a0cd4ac9137349d5a898e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2026 12:32:19 +0100 Subject: [PATCH 544/700] wifi: rt2x00usb: fix devres lifetime USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes). Fix the USB anchor lifetime so that it is released on driver unbind. Fixes: 8b4c0009313f ("rt2x00usb: Use usb anchor to manage URB") Cc: stable@vger.kernel.org # 4.7 Cc: Vishal Thanki Signed-off-by: Johan Hovold Acked-by: Stanislaw Gruszka Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260327113219.1313748-1-johan@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 54599cad78f980..aa5ecade2a4082 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -828,7 +828,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, if (retval) goto exit_free_device; - rt2x00dev->anchor = devm_kmalloc(&usb_dev->dev, + rt2x00dev->anchor = devm_kmalloc(&usb_intf->dev, sizeof(struct usb_anchor), GFP_KERNEL); if (!rt2x00dev->anchor) { From ea245d78dec594372e27d8c79616baf49e98a4a1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 30 Mar 2026 11:14:13 +0200 Subject: [PATCH 545/700] net: rfkill: prevent unlimited numbers of rfkill events from being created Userspace can create an unlimited number of rfkill events if the system is so configured, while not consuming them from the rfkill file descriptor, causing a potential out of memory situation. Prevent this from bounding the number of pending rfkill events at a "large" number (i.e. 1000) to prevent abuses like this. Cc: Johannes Berg Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Cc: stable Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026033013-disfigure-scroll-e25e@gregkh Signed-off-by: Johannes Berg --- net/rfkill/core.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 2444237bc36a44..4827e1fb8804ad 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -73,11 +73,14 @@ struct rfkill_int_event { struct rfkill_event_ext ev; }; +/* Max rfkill events that can be "in-flight" for one data source */ +#define MAX_RFKILL_EVENT 1000 struct rfkill_data { struct list_head list; struct list_head events; struct mutex mtx; wait_queue_head_t read_wait; + u32 event_count; bool input_handler; u8 max_size; }; @@ -255,10 +258,12 @@ static void rfkill_global_led_trigger_unregister(void) } #endif /* CONFIG_RFKILL_LEDS */ -static void rfkill_fill_event(struct rfkill_event_ext *ev, - struct rfkill *rfkill, - enum rfkill_operation op) +static int rfkill_fill_event(struct rfkill_int_event *int_ev, + struct rfkill *rfkill, + struct rfkill_data *data, + enum rfkill_operation op) { + struct rfkill_event_ext *ev = &int_ev->ev; unsigned long flags; ev->idx = rfkill->idx; @@ -271,6 +276,15 @@ static void rfkill_fill_event(struct rfkill_event_ext *ev, RFKILL_BLOCK_SW_PREV)); ev->hard_block_reasons = rfkill->hard_block_reasons; spin_unlock_irqrestore(&rfkill->lock, flags); + + scoped_guard(mutex, &data->mtx) { + if (data->event_count++ > MAX_RFKILL_EVENT) { + data->event_count--; + return -ENOSPC; + } + list_add_tail(&int_ev->list, &data->events); + } + return 0; } static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op) @@ -282,10 +296,10 @@ static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op) ev = kzalloc_obj(*ev); if (!ev) continue; - rfkill_fill_event(&ev->ev, rfkill, op); - mutex_lock(&data->mtx); - list_add_tail(&ev->list, &data->events); - mutex_unlock(&data->mtx); + if (rfkill_fill_event(ev, rfkill, data, op)) { + kfree(ev); + continue; + } wake_up_interruptible(&data->read_wait); } } @@ -1186,10 +1200,8 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) if (!ev) goto free; rfkill_sync(rfkill); - rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); - mutex_lock(&data->mtx); - list_add_tail(&ev->list, &data->events); - mutex_unlock(&data->mtx); + if (rfkill_fill_event(ev, rfkill, data, RFKILL_OP_ADD)) + kfree(ev); } list_add(&data->list, &rfkill_fds); mutex_unlock(&rfkill_global_mutex); @@ -1259,6 +1271,7 @@ static ssize_t rfkill_fop_read(struct file *file, char __user *buf, ret = -EFAULT; list_del(&ev->list); + data->event_count--; kfree(ev); out: mutex_unlock(&data->mtx); From fd495be8aa198a0d57069c37a9b44ee9e86a7486 Mon Sep 17 00:00:00 2001 From: songxiebing Date: Wed, 25 Mar 2026 10:17:52 +0800 Subject: [PATCH 546/700] ASoC: intel: avs: Fix type mismatch in variable assignment The input parameter requirement for snd_pcm_format_physical_with is snd_pcm_format_t,but params->codec.format is __u32, resulting in a mismatch error: sparse warnings: (new ones prefixed by >>) >> sound/soc/intel/avs/probes.c:147:58: sparse: sparse: incorrect type in argument 1 (different base types) @@ expected restricted snd_pcm_format_t [usertype] format @@ got unsigned int [usertype] format @@ sound/soc/intel/avs/probes.c:147:58: sparse: expected restricted snd_pcm_format_t [usertype] format sound/soc/intel/avs/probes.c:147:58: sparse: got unsigned int [usertype] format So here, the format is cast to snd_pcm_format_t. Signed-off-by: songxiebing Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512190032.hnwn9mCV-lkp@intel.com/ Link: https://patch.msgid.link/20260325021752.238203-1-songxiebing@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/intel/avs/probes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 74096236984a00..099119ad28b3cb 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -144,7 +144,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); if (ret < 0) return ret; - bps = snd_pcm_format_physical_width(params->codec.format); + bps = snd_pcm_format_physical_width((__force snd_pcm_format_t)params->codec.format); if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); From 5a1140404cbf7ba40137dfb1fb96893aa9a67d68 Mon Sep 17 00:00:00 2001 From: Nathan Rebello Date: Tue, 7 Apr 2026 02:39:58 -0400 Subject: [PATCH 547/700] usb: typec: ucsi: skip connector validation before init Notifications can arrive before ucsi_init() has populated ucsi->cap.num_connectors via GET_CAPABILITY. At that point num_connectors is still 0, causing all valid connector numbers to be incorrectly rejected as bogus. Skip the bounds check when num_connectors is 0 (not yet initialized). Pre-init notifications are already handled safely by the early-event guard in ucsi_connector_change(). Reported-by: Takashi Iwai Fixes: d2d8c17ac01a ("usb: typec: ucsi: validate connector number in ucsi_notify_common()") Cc: stable@vger.kernel.org Signed-off-by: Nathan Rebello Tested-by: Takashi Iwai Link: https://patch.msgid.link/20260407063958.863-1-nathan.c.rebello@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 8333bdaf55668d..46262ee0d1923c 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -44,7 +44,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) return; if (UCSI_CCI_CONNECTOR(cci)) { - if (UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors) + if (!ucsi->cap.num_connectors || + UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors) ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); else dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n", From 1f8fd9490e3184e9a2394df2e682901a1d57ce71 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Tue, 7 Apr 2026 17:55:08 +0800 Subject: [PATCH 548/700] spi: zynq-qspi: Simplify clock handling with devm_clk_get_enabled() Replace devm_clk_get() followed by clk_prepare_enable() with devm_clk_get_enabled() for both "pclk" and "ref_clk". This removes the need for explicit clock enable and disable calls, as the managed API automatically disables the clocks on device removal or probe failure. Remove the now-unnecessary clk_disable_unprepare() calls from the probe error paths and the remove callback. Simplify error handling by jumping directly to the remove_ctlr label. Signed-off-by: Pei Xiao Acked-by: Michal Simek Link: https://patch.msgid.link/24043625f89376da36feca2408f990a85be7ab36.1775555500.git.xiaopei01@kylinos.cn Signed-off-by: Mark Brown --- drivers/spi/spi-zynq-qspi.c | 42 ++++++------------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index 5232483c4a3a8f..af252500195ce2 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -381,21 +381,10 @@ static int zynq_qspi_setup_op(struct spi_device *spi) { struct spi_controller *ctlr = spi->controller; struct zynq_qspi *qspi = spi_controller_get_devdata(ctlr); - int ret; if (ctlr->busy) return -EBUSY; - ret = clk_enable(qspi->refclk); - if (ret) - return ret; - - ret = clk_enable(qspi->pclk); - if (ret) { - clk_disable(qspi->refclk); - return ret; - } - zynq_qspi_write(qspi, ZYNQ_QSPI_ENABLE_OFFSET, ZYNQ_QSPI_ENABLE_ENABLE_MASK); @@ -661,7 +650,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) goto remove_ctlr; } - xqspi->pclk = devm_clk_get(&pdev->dev, "pclk"); + xqspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xqspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); ret = PTR_ERR(xqspi->pclk); @@ -670,36 +659,24 @@ static int zynq_qspi_probe(struct platform_device *pdev) init_completion(&xqspi->data_completion); - xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk"); + xqspi->refclk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); if (IS_ERR(xqspi->refclk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); ret = PTR_ERR(xqspi->refclk); goto remove_ctlr; } - ret = clk_prepare_enable(xqspi->pclk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable APB clock.\n"); - goto remove_ctlr; - } - - ret = clk_prepare_enable(xqspi->refclk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto clk_dis_pclk; - } - xqspi->irq = platform_get_irq(pdev, 0); if (xqspi->irq < 0) { ret = xqspi->irq; - goto clk_dis_all; + goto remove_ctlr; } ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq, 0, pdev->name, xqspi); if (ret != 0) { ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto clk_dis_all; + goto remove_ctlr; } ret = of_property_read_u32(np, "num-cs", @@ -709,7 +686,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) } else if (num_cs > ZYNQ_QSPI_MAX_NUM_CS) { ret = -EINVAL; dev_err(&pdev->dev, "only 2 chip selects are available\n"); - goto clk_dis_all; + goto remove_ctlr; } else { ctlr->num_chipselect = num_cs; } @@ -728,15 +705,11 @@ static int zynq_qspi_probe(struct platform_device *pdev) ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) { dev_err(&pdev->dev, "devm_spi_register_controller failed\n"); - goto clk_dis_all; + goto remove_ctlr; } return ret; -clk_dis_all: - clk_disable_unprepare(xqspi->refclk); -clk_dis_pclk: - clk_disable_unprepare(xqspi->pclk); remove_ctlr: spi_controller_put(ctlr); @@ -758,9 +731,6 @@ static void zynq_qspi_remove(struct platform_device *pdev) struct zynq_qspi *xqspi = platform_get_drvdata(pdev); zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); - - clk_disable_unprepare(xqspi->refclk); - clk_disable_unprepare(xqspi->pclk); } static const struct of_device_id zynq_qspi_of_match[] = { From c5408d818316061d6063c11a4f47f1ba25a3a708 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 7 Apr 2026 10:54:58 +0200 Subject: [PATCH 549/700] ASoC: Intel: avs: Fix memory leak in avs_register_i2s_test_boards() Caller is responsible for freeing array allocated with parse_int_array(). Found out by Coverity. Fixes: 7d859189de13 ("ASoC: Intel: avs: Allow to specify custom configurations with i2s_test") Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260407085459.400628-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/board_selection.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 8a46285181fa04..dd0e7cd5a7dd7e 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -520,7 +520,8 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev) if (num_elems > max_ssps) { dev_err(adev->dev, "board supports only %d SSP, %d specified\n", max_ssps, num_elems); - return -EINVAL; + ret = -EINVAL; + goto exit; } for (ssp_port = 0; ssp_port < num_elems; ssp_port++) { @@ -528,11 +529,13 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev) for_each_set_bit(tdm_slot, &tdm_slots, 16) { ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot); if (ret) - return ret; + goto exit; } } - return 0; +exit: + kfree(array); + return ret; } static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach) From c2812c0cb909211a1d2e7cec862406e32833b9de Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 7 Apr 2026 14:14:53 +0200 Subject: [PATCH 550/700] MAINTAINERS, mailmap: Change Ulf Hansson's email Change my email in MAINTAINERS and add a few entries in mailmap to start using ulfh@kernel.org. Signed-off-by: Ulf Hansson --- .mailmap | 2 ++ MAINTAINERS | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.mailmap b/.mailmap index 2d04aeba68b40b..22c5ab1c5d55cb 100644 --- a/.mailmap +++ b/.mailmap @@ -849,6 +849,8 @@ Tvrtko Ursulin Tvrtko Ursulin Tycho Andersen Tzung-Bi Shih +Ulf Hansson +Ulf Hansson Umang Jain Uwe Kleine-König Uwe Kleine-König diff --git a/MAINTAINERS b/MAINTAINERS index c3fe46d7c4bc43..7167dcea737d61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6716,7 +6716,7 @@ F: include/linux/platform_data/cpuidle-exynos.h CPUIDLE DRIVER - ARM PSCI M: Lorenzo Pieralisi M: Sudeep Holla -M: Ulf Hansson +M: Ulf Hansson L: linux-pm@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported @@ -6724,7 +6724,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git F: drivers/cpuidle/cpuidle-psci.c CPUIDLE DRIVER - ARM PSCI PM DOMAIN -M: Ulf Hansson +M: Ulf Hansson L: linux-pm@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported @@ -6733,7 +6733,7 @@ F: drivers/cpuidle/cpuidle-psci-domain.c F: drivers/cpuidle/cpuidle-psci.h CPUIDLE DRIVER - DT IDLE PM DOMAIN -M: Ulf Hansson +M: Ulf Hansson L: linux-pm@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git @@ -10729,7 +10729,7 @@ F: Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.yaml F: drivers/i2c/muxes/i2c-demux-pinctrl.c GENERIC PM DOMAINS -M: Ulf Hansson +M: Ulf Hansson L: linux-pm@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/power/power?domain* @@ -18089,7 +18089,7 @@ F: drivers/mmc/host/mmc_spi.c F: include/linux/spi/mmc_spi.h MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM -M: Ulf Hansson +M: Ulf Hansson L: linux-mmc@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git @@ -24696,7 +24696,7 @@ F: drivers/media/i2c/imx415.c SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky M: Alex Dubov -M: Ulf Hansson +M: Ulf Hansson L: linux-mmc@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git @@ -27615,7 +27615,7 @@ F: Documentation/fb/uvesafb.rst F: drivers/video/fbdev/uvesafb.* Ux500 CLOCK DRIVERS -M: Ulf Hansson +M: Ulf Hansson L: linux-clk@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained From 8508e9118649f13f7b857e9e10147b241db615d7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Apr 2026 14:33:17 +0200 Subject: [PATCH 551/700] Revert "ALSA: hda/realtek: Add quirk for Gigabyte Technology to fix headphone" This reverts commit 56fbbe096a89ff4b52af78a21a4afd9d94bdcc80. It caused regressions on other Gigabyte models, and looking at the bugzilla entry again, the suggested change appears rather dubious, as incorrectly setting the front mic pin as the headphone. Fixes: 56fbbe096a89 ("ALSA: hda/realtek: Add quirk for Gigabyte Technology to fix headphone") Cc: Reported-by: Marcin Krycki Reported-by: Theodoros Orfanidis Closes: https://lore.kernel.org/CAEfRphPU_ABuVFzaHhspxgp2WAqi7kKNGo4yOOt0zeVFPSj8+Q@mail.gmail.com Link: https://patch.msgid.link/20260407123333.171130-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc662.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 3a943adf908769..5073165d1f3cf9 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -313,7 +313,6 @@ enum { ALC897_FIXUP_HEADSET_MIC_PIN2, ALC897_FIXUP_UNIS_H3C_X500S, ALC897_FIXUP_HEADSET_MIC_PIN3, - ALC897_FIXUP_H610M_HP_PIN, }; static const struct hda_fixup alc662_fixups[] = { @@ -767,13 +766,6 @@ static const struct hda_fixup alc662_fixups[] = { { } }, }, - [ALC897_FIXUP_H610M_HP_PIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x0321403f }, /* HP out */ - { } - }, - }, }; static const struct hda_quirk alc662_fixup_tbl[] = { @@ -823,7 +815,6 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1458, 0xa194, "H610M H V2 DDR4", ALC897_FIXUP_H610M_HP_PIN), SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), From a9b8b18364fffce4c451e6f6fd218fa4ab646705 Mon Sep 17 00:00:00 2001 From: Muhammad Alifa Ramdhan Date: Fri, 3 Apr 2026 09:36:17 +0800 Subject: [PATCH 552/700] net/tls: fix use-after-free in -EBUSY error path of tls_do_encryption The -EBUSY handling in tls_do_encryption(), introduced by commit 859054147318 ("net: tls: handle backlogging of crypto requests"), has a use-after-free due to double cleanup of encrypt_pending and the scatterlist entry. When crypto_aead_encrypt() returns -EBUSY, the request is enqueued to the cryptd backlog and the async callback tls_encrypt_done() will be invoked upon completion. That callback unconditionally restores the scatterlist entry (sge->offset, sge->length) and decrements ctx->encrypt_pending. However, if tls_encrypt_async_wait() returns an error, the synchronous error path in tls_do_encryption() performs the same cleanup again, double-decrementing encrypt_pending and double-restoring the scatterlist. The double-decrement corrupts the encrypt_pending sentinel (initialized to 1), making tls_encrypt_async_wait() permanently skip the wait for pending async callbacks. A subsequent sendmsg can then free the tls_rec via bpf_exec_tx_verdict() while a cryptd callback is still pending, resulting in a use-after-free when the callback fires on the freed record. Fix this by skipping the synchronous cleanup when the -EBUSY async wait returns an error, since the callback has already handled encrypt_pending and sge restoration. Fixes: 859054147318 ("net: tls: handle backlogging of crypto requests") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Alifa Ramdhan Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260403013617.2838875-1-ramdhan@starlabs.sg Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index dd9dda759bbb8c..83e78a3d1e651e 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -584,6 +584,16 @@ static int tls_do_encryption(struct sock *sk, if (rc == -EBUSY) { rc = tls_encrypt_async_wait(ctx); rc = rc ?: -EINPROGRESS; + /* + * The async callback tls_encrypt_done() has already + * decremented encrypt_pending and restored the sge on + * both success and error. Skip the synchronous cleanup + * below on error, just remove the record and return. + */ + if (rc != -EINPROGRESS) { + list_del(&rec->list); + return rc; + } } if (!rc || rc != -EINPROGRESS) { atomic_dec(&ctx->encrypt_pending); From 82b915051d32a68ea3bbe261c93f5620699ff047 Mon Sep 17 00:00:00 2001 From: Josh Snyder Date: Thu, 2 Apr 2026 16:23:38 -0700 Subject: [PATCH 553/700] tick/nohz: Fix inverted return value in check_tick_dependency() fast path Commit 56534673cea7f ("tick/nohz: Optimize check_tick_dependency() with early return") added a fast path that returns !val when the tick_stop tracepoint is disabled. This is inverted: the slow path returns true when a dependency IS found (val != 0), but !val returns true when val is zero (no dependency). The result is that can_stop_full_tick() sees "dependency found" when there are none, and the tick never stops on nohz_full CPUs. Fix this by returning !!val instead of !val, matching the slow-path semantics. Fixes: 56534673cea7f ("tick/nohz: Optimize check_tick_dependency() with early return") Signed-off-by: Josh Snyder Signed-off-by: Thomas Gleixner Assisted-by: Claude:claude-opus-4-6 Link: https://patch.msgid.link/20260402-fix-idle-tick2-v1-1-eecb589649d3@code406.com --- kernel/time/tick-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index f7907fadd63f21..36449f0010a452 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -345,7 +345,7 @@ static bool check_tick_dependency(atomic_t *dep) int val = atomic_read(dep); if (likely(!tracepoint_enabled(tick_stop))) - return !val; + return !!val; if (val & TICK_DEP_MASK_POSIX_TIMER) { trace_tick_stop(0, TICK_DEP_MASK_POSIX_TIMER); From b1baaff4e5231f0d7db209f72a43112cd2d29257 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 7 Apr 2026 16:27:19 +0300 Subject: [PATCH 554/700] regulator: bd71828-regulator.c: Fix LDON-HEAD mode The ROHM BD72720 supports so called LDON-HEAD -mode, in which the buck10 is expected to be supplying power for an LDO. In this mode, the buck10 voltage will follow what is set for the LDO, on order to lower the power-loss in the LDO. This hardware configuration can be adverticed via the device-tree. When this is done, the Linux driver should omit registering the voltage control operations for the buck10, because the voltage control is now done by the hardware. This is done by modifying the buck10 regulator descriptor, before passing it to the regulator registration functions. There is an off-by-one error when the regulator descriptor array is indexed, and wrong descriptor is modified causing the LDO1 operations to be modified instead of the BUCK10 operations. Fix this by correcting the indexing. Signed-off-by: Matti Vaittinen Fixes: f16a9d76a71d ("regulator: bd71828: Support ROHM BD72720") Link: https://patch.msgid.link/e7eef0bd407522ae5d9b7d0c4ec43f40b1dba833.1775565148.git.mazziesaccount@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/bd71828-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c index c8f3343cfe23e6..473beb4399d9db 100644 --- a/drivers/regulator/bd71828-regulator.c +++ b/drivers/regulator/bd71828-regulator.c @@ -785,7 +785,7 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { }, }; -#define BD72720_BUCK10_DESC_INDEX 10 +#define BD72720_BUCK10_DESC_INDEX 9 #define BD72720_NUM_BUCK_VOLTS 0x100 #define BD72720_NUM_LDO_VOLTS 0x100 #define BD72720_NUM_LDO12346_VOLTS 0x80 From 3ffe5eb4a5f248c0d4b849f050af973396656f85 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 7 Apr 2026 18:17:21 +0200 Subject: [PATCH 555/700] KVM: s390: vsie: Fix races with partial gmap invalidations Introduce a new boolean flag, used for shadow gmaps, to keep track of whether the gmap has been invalidated, either partially or totally. Use the new flag to check whether shadow gmap invalidations happened during shadowing. In such cases, abort whatever was going on, return -EAGAIN and let the caller try again. Fixes: 19d6c5b80443 ("KVM: s390: vsie: Fix unshadowing while shadowing") Signed-off-by: Claudio Imbrenda Message-ID: <20260407161721.247044-1-imbrenda@linux.ibm.com> --- arch/s390/kvm/gaccess.c | 9 +++++---- arch/s390/kvm/gmap.c | 3 +++ arch/s390/kvm/gmap.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 53a8550e7102e8..290e03a13a9562 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1449,7 +1449,7 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union pgste_set_unlock(ptep_h, pgste); if (rc) return rc; - if (!sg->parent) + if (sg->invalidated) return -EAGAIN; newpte = _pte(f->pfn, 0, !p, 0); @@ -1479,7 +1479,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni do { /* _gmap_crstep_xchg_atomic() could have unshadowed this shadow gmap */ - if (!sg->parent) + if (sg->invalidated) return -EAGAIN; oldcrste = READ_ONCE(*host); newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, f->writable, !p); @@ -1492,7 +1492,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni if (!newcrste.h.p && !f->writable) return -EOPNOTSUPP; } while (!_gmap_crstep_xchg_atomic(sg->parent, host, oldcrste, newcrste, f->gfn, false)); - if (!sg->parent) + if (sg->invalidated) return -EAGAIN; newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, 0, !p); @@ -1545,7 +1545,7 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg, entries[i].pfn, i + 1, entries[i].writable); if (rc) return rc; - if (!sg->parent) + if (sg->invalidated) return -EAGAIN; } @@ -1601,6 +1601,7 @@ static inline int _gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, scoped_guard(spinlock, &parent->children_lock) { if (READ_ONCE(sg->parent) != parent) return -EAGAIN; + sg->invalidated = false; rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk); } if (rc == -ENOMEM) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 645c32c767d24b..0111d31e038656 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -181,6 +181,7 @@ void gmap_remove_child(struct gmap *child) list_del(&child->list); child->parent = NULL; + child->invalidated = true; } /** @@ -1069,6 +1070,7 @@ static void gmap_unshadow_level(struct gmap *sg, gfn_t r_gfn, int level) if (level > TABLE_TYPE_PAGE_TABLE) align = 1UL << (11 * level + _SEGMENT_SHIFT); kvm_s390_vsie_gmap_notifier(sg, ALIGN_DOWN(gaddr, align), ALIGN(gaddr + 1, align)); + sg->invalidated = true; if (dat_entry_walk(NULL, r_gfn, sg->asce, 0, level, &crstep, &ptep)) return; if (ptep) { @@ -1174,6 +1176,7 @@ static inline int __gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, s scoped_guard(spinlock, &parent->children_lock) { if (READ_ONCE(sg->parent) != parent) return -EAGAIN; + sg->invalidated = false; for (i = 0; i < CRST_TABLE_PAGES; i++) { if (!context->f[i].valid) continue; diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 579399ef54803d..31ea13fda142bc 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -60,6 +60,7 @@ enum gmap_flags { struct gmap { unsigned long flags; unsigned char edat_level; + bool invalidated; struct kvm *kvm; union asce asce; struct list_head list; From 705355a82b8ea86e0afe44560e970eedc74c05d3 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Mon, 6 Apr 2026 14:40:42 +0530 Subject: [PATCH 556/700] MAINTAINERS: Update AMD SPI driver maintainers Due to additional responsibilities, Raju Rangoju will no longer be supporting AMD SPI driver. Maintenance will be handled by Krishnamoorthi going forward. Cc: Krishnamoorthi M Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20260406091042.4065767-1-Raju.Rangoju@amd.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c3fe46d7c4bc43..213f6a294a9fad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1272,6 +1272,7 @@ F: drivers/hid/amd-sfh-hid/ AMD SPI DRIVER M: Raju Rangoju +M: Krishnamoorthi M L: linux-spi@vger.kernel.org S: Supported F: drivers/spi/spi-amd-pci.c From 3666dc0c47c399695d01fde7c36e08b14f834fa0 Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Fri, 3 Apr 2026 15:36:17 +0530 Subject: [PATCH 557/700] ASoC: amd: ps: fix the pcm device numbering for acp pdm dmic Fixed PCM device numbering is required for acp pdm dmic pcm device to have a common UCM changes. Set the 'use_dai_pcm_id' flag true in acp pdm dma driver for acp 6.3 platform. This will fix the pcm device numbering based on dai_link->id. Fixes: 33cea6bbe488 ("ASoC: amd: add acp6.2 pdm platform driver") Signed-off-by: Syed Saba Kareem Fixes: tag. Link: https://patch.msgid.link/20260403100624.676953-1-syed.sabakareem@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/ps-pdm-dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index c6cd844d458c87..04c014349347ef 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -352,6 +352,7 @@ static const struct snd_soc_component_driver acp63_pdm_component = { .hw_params = acp63_pdm_dma_hw_params, .pointer = acp63_pdm_dma_pointer, .pcm_new = acp63_pdm_dma_new, + .use_dai_pcm_id = true, }; static int acp63_pdm_audio_probe(struct platform_device *pdev) From 7596459f3c93d8d45a1bf12d4d7526b50c15baa2 Mon Sep 17 00:00:00 2001 From: Vinay Belgaumkar Date: Tue, 31 Mar 2026 18:27:10 -0700 Subject: [PATCH 558/700] drm/xe: Fix bug in idledly unit conversion We only need to convert to picosecond units before writing to RING_IDLEDLY. Fixes: 7c53ff050ba8 ("drm/xe: Apply Wa_16023105232") Cc: Tangudu Tilak Tirumalesh Acked-by: Tangudu Tilak Tirumalesh Signed-off-by: Vinay Belgaumkar Link: https://patch.msgid.link/20260401012710.4165547-1-vinay.belgaumkar@intel.com (cherry picked from commit 13743bd628bc9d9a0e2fe53488b2891aedf7cc74) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_hw_engine.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 688d645e0e735c..aa0b7a427f0bbc 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -595,9 +595,8 @@ static void adjust_idledly(struct xe_hw_engine *hwe) maxcnt *= maxcnt_units_ns; if (xe_gt_WARN_ON(gt, idledly >= maxcnt || inhibit_switch)) { - idledly = DIV_ROUND_CLOSEST(((maxcnt - 1) * maxcnt_units_ns), + idledly = DIV_ROUND_CLOSEST(((maxcnt - 1) * 1000), idledly_units_ps); - idledly = DIV_ROUND_CLOSEST(idledly, 1000); xe_mmio_write32(>->mmio, RING_IDLEDLY(hwe->mmio_base), idledly); } } From 95aca8602ef70ffd3d971675751c81826e124f90 Mon Sep 17 00:00:00 2001 From: John Pavlick Date: Mon, 6 Apr 2026 13:23:33 +0000 Subject: [PATCH 559/700] net: sfp: add quirks for Hisense and HSGQ GPON ONT SFP modules Several GPON ONT SFP sticks based on Realtek RTL960x report 1000BASE-LX at 1300MBd in their EEPROM but can operate at 2500base-X. On hosts capable of 2500base-X (e.g. Banana Pi R3 / MT7986), the kernel negotiates only 1G because it trusts the incorrect EEPROM data. Add quirks for: - Hisense-Leox LXT-010S-H - Hisense ZNID-GPON-2311NA - HSGQ HSGQ-XPON-Stick Each quirk advertises 2500base-X and ignores TX_FAULT during the module's ~40s Linux boot time. Tested on Banana Pi R3 (MT7986) with OpenWrt 25.12.1, confirmed 2.5Gbps link and full throughput with flow offloading. Reviewed-by: Russell King (Oracle) Suggested-by: Marcin Nita Signed-off-by: John Pavlick Link: https://patch.msgid.link/20260406132321.72563-1-jspavlick@posteo.net Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index ce8924613363b1..6b7b8ae15d1065 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -543,6 +543,22 @@ static const struct sfp_quirk sfp_quirks[] = { SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex, sfp_fixup_ignore_tx_fault_and_los), + // Hisense LXT-010S-H is a GPON ONT SFP (sold as LEOX LXT-010S-H) that + // can operate at 2500base-X, but reports 1000BASE-LX / 1300MBd in its + // EEPROM + SFP_QUIRK("Hisense-Leox", "LXT-010S-H", sfp_quirk_2500basex, + sfp_fixup_ignore_tx_fault), + + // Hisense ZNID-GPON-2311NA can operate at 2500base-X, but reports + // 1000BASE-LX / 1300MBd in its EEPROM + SFP_QUIRK("Hisense", "ZNID-GPON-2311NA", sfp_quirk_2500basex, + sfp_fixup_ignore_tx_fault), + + // HSGQ HSGQ-XPON-Stick can operate at 2500base-X, but reports + // 1000BASE-LX / 1300MBd in its EEPROM + SFP_QUIRK("HSGQ", "HSGQ-XPON-Stick", sfp_quirk_2500basex, + sfp_fixup_ignore_tx_fault), + // Lantech 8330-262D-E and 8330-265D can operate at 2500base-X, but // incorrectly report 2500MBd NRZ in their EEPROM. // Some 8330-265D modules have inverted LOS, while all of them report From f2777d5cb5c094e20f2323d953b1740baee5f8e8 Mon Sep 17 00:00:00 2001 From: Johan Alvarado Date: Mon, 6 Apr 2026 07:44:25 +0000 Subject: [PATCH 560/700] net: stmmac: dwmac-motorcomm: fix eFUSE MAC address read failure This patch fixes an issue where reading the MAC address from the eFUSE fails due to a race condition. The root cause was identified by comparing the driver's behavior with a custom U-Boot port. In U-Boot, the MAC address was read successfully every time because the driver was loaded later in the boot process, giving the hardware ample time to initialize. In Linux, reading the eFUSE immediately returns all zeros, resulting in a fallback to a random MAC address. Hardware cold-boot testing revealed that the eFUSE controller requires a short settling time to load its internal data. Adding a 2000-5000us delay after the reset ensures the hardware is fully ready, allowing the native MAC address to be read consistently. Fixes: 02ff155ea281 ("net: stmmac: Add glue driver for Motorcomm YT6801 ethernet controller") Reported-by: Georg Gottleuber Closes: https://lore.kernel.org/24cfefff-1233-4745-8c47-812b502d5d19@tuxedocomputers.com Signed-off-by: Johan Alvarado Reviewed-by: Yao Zi Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/fc5992a4-9532-49c3-8ec1-c2f8c5b84ca1@smtp-relay.sendinblue.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c index 8b45b9cf7202ff..663d87ccfa0f8f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -334,6 +335,13 @@ static int motorcomm_probe(struct pci_dev *pdev, const struct pci_device_id *id) motorcomm_reset(priv); + /* + * After system reset, the eFuse controller needs time to load + * its internal data. Without this delay, eFuse reads return + * all zeros, causing MAC address detection to fail. + */ + usleep_range(2000, 5000); + ret = motorcomm_efuse_read_mac(&pdev->dev, priv, res.mac); if (ret == -ENOENT) { dev_warn(&pdev->dev, "eFuse contains no valid MAC address\n"); From 944b3b734cfbbe9502274c092bc3b8220764cc92 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Apr 2026 17:19:38 -0700 Subject: [PATCH 561/700] net: avoid nul-deref trying to bind mp to incapable device Sashiko points out that we use qops in __net_mp_open_rxq() but never validate they are null. This was introduced when check was moved from netdev_rx_queue_restart(). Look at ops directly instead of the locking config. qops imply netdev_need_ops_lock(). We used netdev_need_ops_lock() initially to signify that the real_num_rx_queues check below is safe without rtnl_lock, but I'm not sure if this is actually clear to most people, anyway. Fixes: da7772a2b4ad ("net: move mp->rx_page_size validation to __net_mp_open_rxq()") Acked-by: Daniel Borkmann Reviewed-by: Mina Almasry Link: https://patch.msgid.link/20260404001938.2425670-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/netdev_rx_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c index 668a90658f25ad..05fd2875d72591 100644 --- a/net/core/netdev_rx_queue.c +++ b/net/core/netdev_rx_queue.c @@ -117,7 +117,7 @@ int __net_mp_open_rxq(struct net_device *dev, unsigned int rxq_idx, struct netdev_rx_queue *rxq; int ret; - if (!netdev_need_ops_lock(dev)) + if (!qops) return -EOPNOTSUPP; if (rxq_idx >= dev->real_num_rx_queues) { From efaa71faf212324ecbf6d5339e9717fe53254f58 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 5 Apr 2026 22:29:19 +0100 Subject: [PATCH 562/700] selftests: net: bridge_vlan_mcast: wait for h1 before querier check The querier-interval test adds h1 (currently a slave of the VRF created by simple_if_init) to a temporary bridge br1 acting as an outside IGMP querier. The kernel VRF driver (drivers/net/vrf.c) calls cycle_netdev() on every slave add and remove, toggling the interface admin-down then up. Phylink takes the PHY down during the admin-down half of that cycle. Since h1 and swp1 are cable-connected, swp1 also loses its link may need several seconds to re-negotiate. Use setup_wait_dev $h1 0 which waits for h1 to return to UP state, so the test can rely on the link being back up at this point. Fixes: 4d8610ee8bd77 ("selftests: net: bridge: add vlan mcast_querier_interval tests") Signed-off-by: Daniel Golle Reviewed-by: Alexander Sverdlin Link: https://patch.msgid.link/c830f130860fd2efae08bfb9e5b25fd028e58ce5.1775424423.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh index 72dfbeaf56b923..e8031f68200adf 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh @@ -414,6 +414,7 @@ vlmc_querier_intvl_test() bridge vlan add vid 10 dev br1 self pvid untagged ip link set dev $h1 master br1 ip link set dev br1 up + setup_wait_dev $h1 0 bridge vlan add vid 10 dev $h1 master bridge vlan global set vid 10 dev br1 mcast_snooping 1 mcast_querier 1 sleep 2 From c3812651b522fe8437ebb7063b75ddb95b571643 Mon Sep 17 00:00:00 2001 From: Andrea Mayer Date: Sat, 4 Apr 2026 02:44:04 +0200 Subject: [PATCH 563/700] seg6: separate dst_cache for input and output paths in seg6 lwtunnel The seg6 lwtunnel uses a single dst_cache per encap route, shared between seg6_input_core() and seg6_output_core(). These two paths can perform the post-encap SID lookup in different routing contexts (e.g., ip rules matching on the ingress interface, or VRF table separation). Whichever path runs first populates the cache, and the other reuses it blindly, bypassing its own lookup. Fix this by splitting the cache into cache_input and cache_output, so each path maintains its own cached dst independently. Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels") Cc: stable@vger.kernel.org Signed-off-by: Andrea Mayer Reviewed-by: Nicolas Dichtel Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260404004405.4057-2-andrea.mayer@uniroma2.it Signed-off-by: Jakub Kicinski --- net/ipv6/seg6_iptunnel.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 3e1b9991131a23..d6a0f7df90807f 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -48,7 +48,8 @@ static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) } struct seg6_lwt { - struct dst_cache cache; + struct dst_cache cache_input; + struct dst_cache cache_output; struct seg6_iptunnel_encap tuninfo[]; }; @@ -488,7 +489,7 @@ static int seg6_input_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(lwtst); local_bh_disable(); - dst = dst_cache_get(&slwt->cache); + dst = dst_cache_get(&slwt->cache_input); local_bh_enable(); err = seg6_do_srh(skb, dst); @@ -504,7 +505,7 @@ static int seg6_input_core(struct net *net, struct sock *sk, /* cache only if we don't create a dst reference loop */ if (!dst->error && lwtst != dst->lwtstate) { local_bh_disable(); - dst_cache_set_ip6(&slwt->cache, dst, + dst_cache_set_ip6(&slwt->cache_input, dst, &ipv6_hdr(skb)->saddr); local_bh_enable(); } @@ -564,7 +565,7 @@ static int seg6_output_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); local_bh_disable(); - dst = dst_cache_get(&slwt->cache); + dst = dst_cache_get(&slwt->cache_output); local_bh_enable(); err = seg6_do_srh(skb, dst); @@ -591,7 +592,7 @@ static int seg6_output_core(struct net *net, struct sock *sk, /* cache only if we don't create a dst reference loop */ if (orig_dst->lwtstate != dst->lwtstate) { local_bh_disable(); - dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); + dst_cache_set_ip6(&slwt->cache_output, dst, &fl6.saddr); local_bh_enable(); } @@ -701,11 +702,13 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, slwt = seg6_lwt_lwtunnel(newts); - err = dst_cache_init(&slwt->cache, GFP_ATOMIC); - if (err) { - kfree(newts); - return err; - } + err = dst_cache_init(&slwt->cache_input, GFP_ATOMIC); + if (err) + goto err_free_newts; + + err = dst_cache_init(&slwt->cache_output, GFP_ATOMIC); + if (err) + goto err_destroy_input; memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); @@ -720,11 +723,20 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, *ts = newts; return 0; + +err_destroy_input: + dst_cache_destroy(&slwt->cache_input); +err_free_newts: + kfree(newts); + return err; } static void seg6_destroy_state(struct lwtunnel_state *lwt) { - dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); + struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwt); + + dst_cache_destroy(&slwt->cache_input); + dst_cache_destroy(&slwt->cache_output); } static int seg6_fill_encap_info(struct sk_buff *skb, From 32dfd742f06a68fac6499a58f52025990c854031 Mon Sep 17 00:00:00 2001 From: Andrea Mayer Date: Sat, 4 Apr 2026 02:44:05 +0200 Subject: [PATCH 564/700] selftests: seg6: add test for dst_cache isolation in seg6 lwtunnel Add a selftest that verifies the dst_cache in seg6 lwtunnel is not shared between the input (forwarding) and output (locally generated) paths. The test creates three namespaces (ns_src, ns_router, ns_dst) connected in a line. An SRv6 encap route on ns_router encapsulates traffic destined to cafe::1 with SID fc00::100. The SID is reachable only for forwarded traffic (from ns_src) via an ip rule matching the ingress interface (iif veth-r0 lookup 100), and blackholed in the main table. The test verifies that: 1. A packet generated locally on ns_router does not reach ns_dst with an empty cache, since the SID is blackholed; 2. A forwarded packet from ns_src populates the input cache from table 100 and reaches ns_dst; 3. A packet generated locally on ns_router still does not reach ns_dst after the input cache is populated, confirming the output path does not reuse the input cache entry. Both the forwarded and local packets are pinned to the same CPU with taskset, since dst_cache is per-cpu. Cc: Shuah Khan Signed-off-by: Andrea Mayer Reviewed-by: Nicolas Dichtel Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260404004405.4057-3-andrea.mayer@uniroma2.it Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/Makefile | 1 + .../selftests/net/srv6_iptunnel_cache.sh | 197 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100755 tools/testing/selftests/net/srv6_iptunnel_cache.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 605c54c0e8a35b..c709523c99c6bc 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -89,6 +89,7 @@ TEST_PROGS := \ srv6_end_x_next_csid_l3vpn_test.sh \ srv6_hencap_red_l3vpn_test.sh \ srv6_hl2encap_red_l2vpn_test.sh \ + srv6_iptunnel_cache.sh \ stress_reuseport_listen.sh \ tcp_fastopen_backup_key.sh \ test_bpf.sh \ diff --git a/tools/testing/selftests/net/srv6_iptunnel_cache.sh b/tools/testing/selftests/net/srv6_iptunnel_cache.sh new file mode 100755 index 00000000000000..62638ab679d9f5 --- /dev/null +++ b/tools/testing/selftests/net/srv6_iptunnel_cache.sh @@ -0,0 +1,197 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# author: Andrea Mayer + +# This test verifies that the seg6 lwtunnel does not share the dst_cache +# between the input (forwarding) and output (locally generated) paths. +# +# A shared dst_cache allows a forwarded packet to populate the cache and a +# subsequent locally generated packet to silently reuse that entry, bypassing +# its own route lookup. To expose this, the SID is made reachable only for +# forwarded traffic (via an ip rule matching iif) and blackholed for everything +# else. A local ping on ns_router must always hit the blackhole; +# if it succeeds after a forwarded packet has populated the +# cache, the bug is confirmed. +# +# Both forwarded and local packets are pinned to the same CPU with taskset, +# since dst_cache is per-cpu. +# +# +# +--------------------+ +--------------------+ +# | ns_src | | ns_dst | +# | | | | +# | veth-s0 | | veth-d0 | +# | fd00::1/64 | | fd01::2/64 | +# +-------+------------+ +----------+---------+ +# | | +# | +--------------------+ | +# | | ns_router | | +# | | | | +# +------------+ veth-r0 veth-r1 +--------------+ +# | fd00::2 fd01::1 | +# +--------------------+ +# +# +# ns_router: encap (main table) +# +---------+---------------------------------------+ +# | dst | action | +# +---------+---------------------------------------+ +# | cafe::1 | encap seg6 mode encap segs fc00::100 | +# +---------+---------------------------------------+ +# +# ns_router: post-encap SID resolution +# +-------+------------+----------------------------+ +# | table | dst | action | +# +-------+------------+----------------------------+ +# | 100 | fc00::100 | via fd01::2 dev veth-r1 | +# +-------+------------+----------------------------+ +# | main | fc00::100 | blackhole | +# +-------+------------+----------------------------+ +# +# ns_router: ip rule +# +------------------+------------------------------+ +# | match | action | +# +------------------+------------------------------+ +# | iif veth-r0 | lookup 100 | +# +------------------+------------------------------+ +# +# ns_dst: SRv6 decap (main table) +# +--------------+----------------------------------+ +# | SID | action | +# +--------------+----------------------------------+ +# | fc00::100 | End.DT6 table 255 (local) | +# +--------------+----------------------------------+ + +source lib.sh + +readonly SID="fc00::100" +readonly DEST="cafe::1" + +readonly SRC_MAC="02:00:00:00:00:01" +readonly RTR_R0_MAC="02:00:00:00:00:02" +readonly RTR_R1_MAC="02:00:00:00:00:03" +readonly DST_MAC="02:00:00:00:00:04" + +cleanup() +{ + cleanup_ns "${NS_SRC}" "${NS_RTR}" "${NS_DST}" +} + +check_prerequisites() +{ + if ! command -v ip &>/dev/null; then + echo "SKIP: ip tool not found" + exit "${ksft_skip}" + fi + + if ! command -v ping &>/dev/null; then + echo "SKIP: ping not found" + exit "${ksft_skip}" + fi + + if ! command -v sysctl &>/dev/null; then + echo "SKIP: sysctl not found" + exit "${ksft_skip}" + fi + + if ! command -v taskset &>/dev/null; then + echo "SKIP: taskset not found" + exit "${ksft_skip}" + fi +} + +setup() +{ + setup_ns NS_SRC NS_RTR NS_DST + + ip link add veth-s0 netns "${NS_SRC}" type veth \ + peer name veth-r0 netns "${NS_RTR}" + ip link add veth-r1 netns "${NS_RTR}" type veth \ + peer name veth-d0 netns "${NS_DST}" + + ip -n "${NS_SRC}" link set veth-s0 address "${SRC_MAC}" + ip -n "${NS_RTR}" link set veth-r0 address "${RTR_R0_MAC}" + ip -n "${NS_RTR}" link set veth-r1 address "${RTR_R1_MAC}" + ip -n "${NS_DST}" link set veth-d0 address "${DST_MAC}" + + # ns_src + ip -n "${NS_SRC}" link set veth-s0 up + ip -n "${NS_SRC}" addr add fd00::1/64 dev veth-s0 nodad + ip -n "${NS_SRC}" -6 route add "${DEST}"/128 via fd00::2 + + # ns_router + ip -n "${NS_RTR}" link set veth-r0 up + ip -n "${NS_RTR}" addr add fd00::2/64 dev veth-r0 nodad + ip -n "${NS_RTR}" link set veth-r1 up + ip -n "${NS_RTR}" addr add fd01::1/64 dev veth-r1 nodad + ip netns exec "${NS_RTR}" sysctl -qw net.ipv6.conf.all.forwarding=1 + + ip -n "${NS_RTR}" -6 route add "${DEST}"/128 \ + encap seg6 mode encap segs "${SID}" dev veth-r0 + ip -n "${NS_RTR}" -6 route add "${SID}"/128 table 100 \ + via fd01::2 dev veth-r1 + ip -n "${NS_RTR}" -6 route add blackhole "${SID}"/128 + ip -n "${NS_RTR}" -6 rule add iif veth-r0 lookup 100 + + # ns_dst + ip -n "${NS_DST}" link set veth-d0 up + ip -n "${NS_DST}" addr add fd01::2/64 dev veth-d0 nodad + ip -n "${NS_DST}" addr add "${DEST}"/128 dev lo nodad + ip -n "${NS_DST}" -6 route add "${SID}"/128 \ + encap seg6local action End.DT6 table 255 dev veth-d0 + ip -n "${NS_DST}" -6 route add fd00::/64 via fd01::1 + + # static neighbors + ip -n "${NS_SRC}" -6 neigh add fd00::2 dev veth-s0 \ + lladdr "${RTR_R0_MAC}" nud permanent + ip -n "${NS_RTR}" -6 neigh add fd00::1 dev veth-r0 \ + lladdr "${SRC_MAC}" nud permanent + ip -n "${NS_RTR}" -6 neigh add fd01::2 dev veth-r1 \ + lladdr "${DST_MAC}" nud permanent + ip -n "${NS_DST}" -6 neigh add fd01::1 dev veth-d0 \ + lladdr "${RTR_R1_MAC}" nud permanent +} + +test_cache_isolation() +{ + RET=0 + + # local ping with empty cache: must fail (SID is blackholed) + if ip netns exec "${NS_RTR}" taskset -c 0 \ + ping -c 1 -W 2 "${DEST}" &>/dev/null; then + echo "SKIP: local ping succeeded, topology broken" + exit "${ksft_skip}" + fi + + # forward from ns_src to populate the input cache + if ! ip netns exec "${NS_SRC}" taskset -c 0 \ + ping -c 1 -W 2 "${DEST}" &>/dev/null; then + echo "SKIP: forwarded ping failed, topology broken" + exit "${ksft_skip}" + fi + + # local ping again: must still fail; if the output path reuses + # the input cache, it bypasses the blackhole and the ping succeeds + if ip netns exec "${NS_RTR}" taskset -c 0 \ + ping -c 1 -W 2 "${DEST}" &>/dev/null; then + echo "FAIL: output path used dst cached by input path" + RET="${ksft_fail}" + else + echo "PASS: output path dst_cache is independent" + fi + + return "${RET}" +} + +if [ "$(id -u)" -ne 0 ]; then + echo "SKIP: Need root privileges" + exit "${ksft_skip}" +fi + +trap cleanup EXIT + +check_prerequisites +setup +test_cache_isolation +exit "${RET}" From 4cda78d6f8bf2b700529f2fbccb994c3e826d7c2 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 7 Apr 2026 12:50:31 +0500 Subject: [PATCH 565/700] Input: uinput - fix circular locking dependency with ff-core A lockdep circular locking dependency warning can be triggered reproducibly when using a force-feedback gamepad with uinput (for example, playing ELDEN RING under Wine with a Flydigi Vader 5 controller): ff->mutex -> udev->mutex -> input_mutex -> dev->mutex -> ff->mutex The cycle is caused by four lock acquisition paths: 1. ff upload: input_ff_upload() holds ff->mutex and calls uinput_dev_upload_effect() -> uinput_request_submit() -> uinput_request_send(), which acquires udev->mutex. 2. device create: uinput_ioctl_handler() holds udev->mutex and calls uinput_create_device() -> input_register_device(), which acquires input_mutex. 3. device register: input_register_device() holds input_mutex and calls kbd_connect() -> input_register_handle(), which acquires dev->mutex. 4. evdev release: evdev_release() calls input_flush_device() under dev->mutex, which calls input_ff_flush() acquiring ff->mutex. Fix this by introducing a new state_lock spinlock to protect udev->state and udev->dev access in uinput_request_send() instead of acquiring udev->mutex. The function only needs to atomically check device state and queue an input event into the ring buffer via uinput_dev_event() -- both operations are safe under a spinlock (ktime_get_ts64() and wake_up_interruptible() do not sleep). This breaks the ff->mutex -> udev->mutex link since a spinlock is a leaf in the lock ordering and cannot form cycles with mutexes. To keep state transitions visible to uinput_request_send(), protect writes to udev->state in uinput_create_device() and uinput_destroy_device() with the same state_lock spinlock. Additionally, move init_completion(&request->done) from uinput_request_send() to uinput_request_submit() before uinput_request_reserve_slot(). Once the slot is allocated, uinput_flush_requests() may call complete() on it at any time from the destroy path, so the completion must be initialised before the request becomes visible. Lock ordering after the fix: ff->mutex -> state_lock (spinlock, leaf) udev->mutex -> state_lock (spinlock, leaf) udev->mutex -> input_mutex -> dev->mutex -> ff->mutex (no back-edge) Fixes: ff462551235d ("Input: uinput - switch to the new FF interface") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/CABXGCsMoxag+kEwHhb7KqhuyxfmGGd0P=tHZyb1uKE0pLr8Hkg@mail.gmail.com/ Signed-off-by: Mikhail Gavrilov Link: https://patch.msgid.link/20260407075031.38351-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 13336a2fd49c8a..a973e82205b5c8 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -57,6 +57,7 @@ struct uinput_device { struct input_dev *dev; struct mutex mutex; enum uinput_state state; + spinlock_t state_lock; wait_queue_head_t waitq; unsigned char ready; unsigned char head; @@ -146,19 +147,15 @@ static void uinput_request_release_slot(struct uinput_device *udev, static int uinput_request_send(struct uinput_device *udev, struct uinput_request *request) { - int retval; + int retval = 0; - retval = mutex_lock_interruptible(&udev->mutex); - if (retval) - return retval; + spin_lock(&udev->state_lock); if (udev->state != UIST_CREATED) { retval = -ENODEV; goto out; } - init_completion(&request->done); - /* * Tell our userspace application about this new request * by queueing an input event. @@ -166,7 +163,7 @@ static int uinput_request_send(struct uinput_device *udev, uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); out: - mutex_unlock(&udev->mutex); + spin_unlock(&udev->state_lock); return retval; } @@ -175,6 +172,13 @@ static int uinput_request_submit(struct uinput_device *udev, { int retval; + /* + * Initialize completion before allocating the request slot. + * Once the slot is allocated, uinput_flush_requests() may + * complete it at any time, so it must be initialized first. + */ + init_completion(&request->done); + retval = uinput_request_reserve_slot(udev, request); if (retval) return retval; @@ -289,7 +293,14 @@ static void uinput_destroy_device(struct uinput_device *udev) struct input_dev *dev = udev->dev; enum uinput_state old_state = udev->state; + /* + * Update state under state_lock so that concurrent + * uinput_request_send() sees the state change before we + * flush pending requests and tear down the device. + */ + spin_lock(&udev->state_lock); udev->state = UIST_NEW_DEVICE; + spin_unlock(&udev->state_lock); if (dev) { name = dev->name; @@ -366,7 +377,9 @@ static int uinput_create_device(struct uinput_device *udev) if (error) goto fail2; + spin_lock(&udev->state_lock); udev->state = UIST_CREATED; + spin_unlock(&udev->state_lock); return 0; @@ -384,6 +397,7 @@ static int uinput_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&newdev->mutex); + spin_lock_init(&newdev->state_lock); spin_lock_init(&newdev->requests_lock); init_waitqueue_head(&newdev->requests_waitq); init_waitqueue_head(&newdev->waitq); From 828ec7f803f41588a120e6d804297e74a482ab9d Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 7 Apr 2026 15:41:48 +0300 Subject: [PATCH 566/700] gpio: bd72720: handle missing regmap Currently the probe does not check whether getting the regmap succeeded. This can cause crash when regmap is used, if it wasn't successfully obtained. Failing to get the regmap is unlikely, especially since this driver is expected to be kicked by the MFD driver only after registering the regmap - but it is still better to handle this gracefully. Signed-off-by: Matti Vaittinen Fixes: e7eef0bd4075 ("regulator: bd71828-regulator.c: Fix LDON-HEAD mode") Link: https://patch.msgid.link/5bfffee380863bcf24f3062e48094c8eb7b1342f.1775565381.git.mazziesaccount@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd72720.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c index 6549dbf4c7ad1f..d0f936ed80af34 100644 --- a/drivers/gpio/gpio-bd72720.c +++ b/drivers/gpio/gpio-bd72720.c @@ -256,6 +256,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev) g->dev = dev; g->chip.parent = parent; g->regmap = dev_get_regmap(parent, NULL); + if (!g->regmap) + return -ENODEV; return devm_gpiochip_add_data(dev, &g->chip, g); } From 22cb174c0af85e83d02a7b44fcc20c77008ee885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 7 Apr 2026 12:35:41 -0300 Subject: [PATCH 567/700] ALSA: tea6330t: move snd_tea6330t_detect() EXPORT_SYMBOL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the remaining standalone snd_tea6330t_detect() EXPORT_SYMBOL() declaration next to its function definition so tea6330t.c follows the usual layout. No functional change intended. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260407-alsa-interwave-pm-v2-1-8dd96c6129e9@gmail.com --- sound/i2c/tea6330t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c index e8c50a036bea7c..5341907b85d190 100644 --- a/sound/i2c/tea6330t.c +++ b/sound/i2c/tea6330t.c @@ -51,6 +51,7 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer) snd_i2c_unlock(bus); return res; } +EXPORT_SYMBOL(snd_tea6330t_detect); #if 0 static void snd_tea6330t_set(struct tea6330t *tea, @@ -356,5 +357,4 @@ int snd_tea6330t_update_mixer(struct snd_card *card, return err; } -EXPORT_SYMBOL(snd_tea6330t_detect); EXPORT_SYMBOL(snd_tea6330t_update_mixer); From 19cbb3e0c27f28feb7781641994226cb2ee206a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 7 Apr 2026 12:35:42 -0300 Subject: [PATCH 568/700] ALSA: tea6330t: add mixer state restore helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The InterWave STB variant uses a TEA6330T mixer on its private I2C bus. The mixer state is cached in software, but there is no helper to push that register image back to hardware after system resume. Add a small restore helper that reapplies the cached TEA6330T register image to the device so board drivers can restore the external mixer state as part of their PM resume path. Take snd_i2c_lock() around the full device lookup and restore sequence so the bus device list traversal is also protected. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260407-alsa-interwave-pm-v2-2-8dd96c6129e9@gmail.com --- include/sound/tea6330t.h | 1 + sound/i2c/tea6330t.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/sound/tea6330t.h b/include/sound/tea6330t.h index 1c77b78f65332a..3a34033d2aa307 100644 --- a/include/sound/tea6330t.h +++ b/include/sound/tea6330t.h @@ -12,5 +12,6 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer); int snd_tea6330t_update_mixer(struct snd_card *card, struct snd_i2c_bus *bus, int equalizer, int fader); +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus); #endif /* __SOUND_TEA6330T_H */ diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c index 5341907b85d190..39c5e87c6ab050 100644 --- a/sound/i2c/tea6330t.c +++ b/sound/i2c/tea6330t.c @@ -356,5 +356,42 @@ int snd_tea6330t_update_mixer(struct snd_card *card, snd_i2c_device_free(device); return err; } - EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +int snd_tea6330t_restore_mixer(struct snd_i2c_bus *bus) +{ + struct snd_i2c_device *device; + struct tea6330t *tea; + unsigned char bytes[7]; + unsigned int idx; + int err; + + if (!bus) + return -EINVAL; + + snd_i2c_lock(bus); + list_for_each_entry(device, &bus->devices, list) { + if (device->addr != TEA6330T_ADDR) + continue; + + tea = device->private_data; + if (!tea) { + err = -EINVAL; + goto unlock; + } + + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx + 1] = tea->regs[idx]; + err = snd_i2c_sendbytes(device, bytes, 7); + err = err < 0 ? err : 0; + goto unlock; + } + + err = -ENODEV; + +unlock: + snd_i2c_unlock(bus); + return err; +} +EXPORT_SYMBOL(snd_tea6330t_restore_mixer); From 6f800c3397b7f64da4c9eb636a1206f8d8636c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 7 Apr 2026 12:35:43 -0300 Subject: [PATCH 569/700] ALSA: interwave: add ISA and PnP suspend and resume callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit interwave still leaves both its ISA and PnP PM callbacks disabled even though the shared GUS suspend and resume path now exists. This board needs InterWave-specific glue around the shared GUS PM path. The attached WSS codec has its own register image that must be saved and restored across suspend, the InterWave-specific GF1 compatibility, decode, MPU401, and emulation settings must be rewritten after the shared GF1 resume path reinitializes the chip, and the probe-detected InterWave memory layout must be restored without rerunning the destructive DRAM/ROM detection path. Track the optional STB TEA6330T bus at probe time, restore its cached mixer state after resume, add resume-safe helpers for the InterWave register and memory-configuration state, and wire both the ISA and PnP front-ends up to the shared GUS PM helpers. The resume path intentionally restores only the cached hardware setup. It does not attempt to preserve sample RAM contents across suspend. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260407-alsa-interwave-pm-v2-3-8dd96c6129e9@gmail.com --- sound/isa/gus/interwave.c | 178 ++++++++++++++++++++++++++++++++------ 1 file changed, 151 insertions(+), 27 deletions(-) diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 18adcd35e117ba..616c11e51a2f60 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -96,6 +96,7 @@ struct snd_interwave { struct snd_gus_card *gus; struct snd_wss *wss; #ifdef SNDRV_STB + struct snd_i2c_bus *i2c_bus; struct resource *i2c_res; #endif unsigned short gus_status_reg; @@ -363,18 +364,30 @@ struct rom_hdr { /* 511 */ unsigned char csum; }; -static void snd_interwave_detect_memory(struct snd_gus_card *gus) +static const unsigned int snd_interwave_memory_configs[] = { + 0x00000001, 0x00000101, 0x01010101, 0x00000401, + 0x04040401, 0x00040101, 0x04040101, 0x00000004, + 0x00000404, 0x04040404, 0x00000010, 0x00001010, + 0x10101010 +}; + +static int snd_interwave_find_memory_config(unsigned int lmct) { - static const unsigned int lmc[13] = - { - 0x00000001, 0x00000101, 0x01010101, 0x00000401, - 0x04040401, 0x00040101, 0x04040101, 0x00000004, - 0x00000404, 0x04040404, 0x00000010, 0x00001010, - 0x10101010 - }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(snd_interwave_memory_configs); i++) { + if (lmct == snd_interwave_memory_configs[i]) + return i; + } + + return -EINVAL; +} +static void snd_interwave_detect_memory(struct snd_gus_card *gus) +{ int bank_pos, pages; unsigned int i, lmct; + int lmc_cfg; int psizes[4]; unsigned char iwave[8]; unsigned char csum; @@ -399,17 +412,20 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) #if 0 dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct); #endif - for (i = 0; i < ARRAY_SIZE(lmc); i++) - if (lmct == lmc[i]) { + lmc_cfg = snd_interwave_find_memory_config(lmct); + if (lmc_cfg >= 0) { #if 0 - dev_dbg(gus->card->dev, "found !!! %i\n", i); + dev_dbg(gus->card->dev, "found !!! %i\n", lmc_cfg); #endif - snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); - snd_interwave_bank_sizes(gus, psizes); - break; - } - if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) - snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, + (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | + lmc_cfg); + snd_interwave_bank_sizes(gus, psizes); + } else if (!gus->gf1.enh_mode) { + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, + (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | + 2); + } for (i = 0; i < 4; i++) { gus->gf1.mem_alloc.banks_8[i].address = gus->gf1.mem_alloc.banks_16[i].address = i << 22; @@ -454,24 +470,66 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) snd_interwave_reset(gus); } +static void __snd_interwave_restore_regs(struct snd_gus_card *gus) +{ + snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); + snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); + snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); +} + +static void snd_interwave_restore_regs(struct snd_gus_card *gus) +{ + scoped_guard(spinlock_irqsave, &gus->reg_lock) + __snd_interwave_restore_regs(gus); +} + +static void snd_interwave_restore_memory(struct snd_gus_card *gus) +{ + unsigned short mem_cfg; + unsigned int lmct = 0; + int i, lmc_cfg; + + if (!gus->gf1.memory) + return; + + for (i = 0; i < 4; i++) + lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8); + + lmc_cfg = snd_interwave_find_memory_config(lmct); + if (lmc_cfg < 0) { + if (!gus->gf1.enh_mode) { + lmc_cfg = 2; + } else { + dev_warn(gus->card->dev, + "cannot restore InterWave memory layout 0x%08x\n", + lmct); + return; + } + } + + scoped_guard(spinlock_irqsave, &gus->reg_lock) { + mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG); + mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg; + mem_cfg = (mem_cfg & 0xff1f) | (4 << 5); + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg); + } +} + static void snd_interwave_init(int dev, struct snd_gus_card *gus) { - /* ok.. some InterWave specific initialization */ + /* Probe-time setup also clears the timer control register. */ scoped_guard(spinlock_irqsave, &gus->reg_lock) { snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); - snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); - snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); - snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); - snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); - snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); - snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); + __snd_interwave_restore_regs(gus); } gus->equal_irq = 1; gus->codec_flag = 1; gus->interwave = 1; gus->max_flag = 1; gus->joystick_dac = joystick_dac[dev]; - } static const struct snd_kcontrol_new snd_interwave_controls[] = { @@ -724,6 +782,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev, err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1); if (err < 0) return err; + iwcard->i2c_bus = i2c_bus; } #endif @@ -828,10 +887,59 @@ static int snd_interwave_isa_probe(struct device *pdev, return 0; } +#ifdef CONFIG_PM +static int snd_interwave_card_suspend(struct snd_card *card) +{ + struct snd_interwave *iwcard = card->private_data; + + iwcard->wss->suspend(iwcard->wss); + return snd_gus_suspend(iwcard->gus); +} + +static int snd_interwave_card_resume(struct snd_card *card) +{ + struct snd_interwave *iwcard = card->private_data; + int err; + + err = snd_gus_resume(iwcard->gus); + if (err < 0) + return err; + + snd_interwave_restore_regs(iwcard->gus); + snd_interwave_restore_memory(iwcard->gus); + iwcard->wss->resume(iwcard->wss); +#ifdef SNDRV_STB + if (iwcard->i2c_bus) { + err = snd_tea6330t_restore_mixer(iwcard->i2c_bus); + if (err < 0) + dev_warn(card->dev, + "failed to restore TEA6330T mixer state: %d\n", + err); + } +#endif + + return 0; +} + +static int snd_interwave_isa_suspend(struct device *pdev, unsigned int dev, + pm_message_t state) +{ + return snd_interwave_card_suspend(dev_get_drvdata(pdev)); +} + +static int snd_interwave_isa_resume(struct device *pdev, unsigned int dev) +{ + return snd_interwave_card_resume(dev_get_drvdata(pdev)); +} +#endif + static struct isa_driver snd_interwave_driver = { .match = snd_interwave_isa_match, .probe = snd_interwave_isa_probe, - /* FIXME: suspend,resume */ +#ifdef CONFIG_PM + .suspend = snd_interwave_isa_suspend, + .resume = snd_interwave_isa_resume, +#endif .driver = { .name = INTERWAVE_DRIVER }, @@ -871,12 +979,28 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int snd_interwave_pnpc_suspend(struct pnp_card_link *pcard, + pm_message_t state) +{ + return snd_interwave_card_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_interwave_pnpc_resume(struct pnp_card_link *pcard) +{ + return snd_interwave_card_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver interwave_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, .name = INTERWAVE_PNP_DRIVER, .id_table = snd_interwave_pnpids, .probe = snd_interwave_pnp_detect, - /* FIXME: suspend,resume */ +#ifdef CONFIG_PM + .suspend = snd_interwave_pnpc_suspend, + .resume = snd_interwave_pnpc_resume, +#endif }; #endif /* CONFIG_PNP */ From 66f6f543283e91b8899b0dd109d8f15a529e8464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 7 Apr 2026 18:13:06 -0300 Subject: [PATCH 570/700] ALSA: i2c: ak4xxx-adda: implement AK4529 reset handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delta 410 uses snd_akm4xxx_reset() both around DFS changes and from its PM callbacks, but the AK4529 case in this helper is still left unimplemented and never drives the codec reset path. The AK4529 datasheet documents register 09h.RSTN as an internal timing reset. Clearing RSTN powers down the ADC and DAC blocks, but does not reinitialize the register map. That matches the existing ak4xxx helper model, which already keeps the desired codec state in the software register cache. Implement AK4529 reset handling by clearing 09h.RSTN on state == 1, then replaying the cached register image and setting RSTN back to 1 on state == 0. This restores cached Delta 410 mixer state after resume and gives the AK4529 DFS-change path a real codec reset sequence. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260407-ak4529-reset-handling-v1-1-b971c18b1a32@gmail.com Signed-off-by: Takashi Iwai --- sound/i2c/other/ak4xxx-adda.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index b24c80410d45e3..bdeb3cef3640e1 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -53,6 +53,31 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state) } } +/* reset procedure for AK4529 */ +static void ak4529_reset(struct snd_akm4xxx *ak, int state) +{ + static const unsigned char regs[] = { + 0x0a, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x0b, 0x0c, 0x08, + }; + unsigned int i; + unsigned char reg; + + if (state) { + snd_akm4xxx_write(ak, 0, 0x09, + snd_akm4xxx_get(ak, 0, 0x09) & ~0x01); + return; + } + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + reg = regs[i]; + snd_akm4xxx_write(ak, 0, reg, + snd_akm4xxx_get(ak, 0, reg)); + } + snd_akm4xxx_write(ak, 0, 0x09, + snd_akm4xxx_get(ak, 0, 0x09) | 0x01); +} + /* reset procedure for AK4355 and AK4358 */ static void ak435X_reset(struct snd_akm4xxx *ak, int state) { @@ -99,7 +124,7 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) ak4524_reset(ak, state); break; case SND_AK4529: - /* FIXME: needed for ak4529? */ + ak4529_reset(ak, state); break; case SND_AK4355: ak435X_reset(ak, state); From 292286b2d229fb732421429b027d38ac3f969383 Mon Sep 17 00:00:00 2001 From: songxiebing Date: Wed, 8 Apr 2026 16:33:11 +0800 Subject: [PATCH 571/700] ALSA: usb-audio: qcom: Fix incorrect type in enable_audio_stream Fix sparse warning: sound/usb/qcom/qc_audio_offload.c:943:27: sparse: incorrect type in argument 2 expected unsigned int val but got snd_pcm_format_t. Explicitly cast pcm_format to unsigned int for snd_mask_leave(). Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604062109.Oxi8JjWW-lkp@intel.com/ Signed-off-by: songxiebing Link: https://patch.msgid.link/20260408083311.774173-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/usb/qcom/qc_audio_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index f161eb29f91178..9c0d370860f3c3 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -947,7 +947,7 @@ static int enable_audio_stream(struct snd_usb_substream *subs, _snd_pcm_hw_params_any(¶ms); m = hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); - snd_mask_leave(m, pcm_format); + snd_mask_leave(m, (__force unsigned int)pcm_format); i = hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS); snd_interval_setinteger(i); From 14a857056466be9d3d907a94e92a704ac1be149b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 4 Apr 2026 12:22:44 +0200 Subject: [PATCH 572/700] sched/deadline: Use revised wakeup rule for dl_server John noted that commit 115135422562 ("sched/deadline: Fix 'stuck' dl_server") unfixed the issue from commit a3a70caf7906 ("sched/deadline: Fix dl_server behaviour"). The issue in commit 115135422562 was for wakeups of the server after the deadline; in which case you *have* to start a new period. The case for a3a70caf7906 is wakeups before the deadline. Now, because the server is effectively running a least-laxity policy, it means that any wakeup during the runnable phase means dl_entity_overflow() will be true. This means we need to adjust the runtime to allow it to still run until the existing deadline expires. Use the revised wakeup rule for dl_defer entities. Fixes: 115135422562 ("sched/deadline: Fix 'stuck' dl_server") Reported-by: John Stultz Signed-off-by: Peter Zijlstra (Intel) Acked-by: Juri Lelli Tested-by: John Stultz Link: https://patch.msgid.link/20260404102244.GB22575@noisy.programming.kicks-ass.net --- kernel/sched/deadline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index d08b004293234d..674de6a48551b5 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1027,7 +1027,7 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) if (dl_time_before(dl_se->deadline, rq_clock(rq)) || dl_entity_overflow(dl_se, rq_clock(rq))) { - if (unlikely(!dl_is_implicit(dl_se) && + if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && !dl_time_before(dl_se->deadline, rq_clock(rq)) && !is_dl_boosted(dl_se))) { update_dl_revised_wakeup(dl_se, rq); From 4c71fd099513bfa8acab529b626e1f0097b76061 Mon Sep 17 00:00:00 2001 From: Sebastian Brzezinka Date: Wed, 1 Apr 2026 12:10:07 +0200 Subject: [PATCH 573/700] drm/i915/gt: fix refcount underflow in intel_engine_park_heartbeat A use-after-free / refcount underflow is possible when the heartbeat worker and intel_engine_park_heartbeat() race to release the same engine->heartbeat.systole request. The heartbeat worker reads engine->heartbeat.systole and calls i915_request_put() on it when the request is complete, but clears the pointer in a separate, non-atomic step. Concurrently, a request retirement on another CPU can drop the engine wakeref to zero, triggering __engine_park() -> intel_engine_park_heartbeat(). If the heartbeat timer is pending at that point, cancel_delayed_work() returns true and intel_engine_park_heartbeat() reads the stale non-NULL systole pointer and calls i915_request_put() on it again, causing a refcount underflow: ``` <4> [487.221889] Workqueue: i915-unordered engine_retire [i915] <4> [487.222640] RIP: 0010:refcount_warn_saturate+0x68/0xb0 ... <4> [487.222707] Call Trace: <4> [487.222711] <4> [487.222716] intel_engine_park_heartbeat.part.0+0x6f/0x80 [i915] <4> [487.223115] intel_engine_park_heartbeat+0x25/0x40 [i915] <4> [487.223566] __engine_park+0xb9/0x650 [i915] <4> [487.223973] ____intel_wakeref_put_last+0x2e/0xb0 [i915] <4> [487.224408] __intel_wakeref_put_last+0x72/0x90 [i915] <4> [487.224797] intel_context_exit_engine+0x7c/0x80 [i915] <4> [487.225238] intel_context_exit+0xf1/0x1b0 [i915] <4> [487.225695] i915_request_retire.part.0+0x1b9/0x530 [i915] <4> [487.226178] i915_request_retire+0x1c/0x40 [i915] <4> [487.226625] engine_retire+0x122/0x180 [i915] <4> [487.227037] process_one_work+0x239/0x760 <4> [487.227060] worker_thread+0x200/0x3f0 <4> [487.227068] ? __pfx_worker_thread+0x10/0x10 <4> [487.227075] kthread+0x10d/0x150 <4> [487.227083] ? __pfx_kthread+0x10/0x10 <4> [487.227092] ret_from_fork+0x3d4/0x480 <4> [487.227099] ? __pfx_kthread+0x10/0x10 <4> [487.227107] ret_from_fork_asm+0x1a/0x30 <4> [487.227141] ``` Fix this by replacing the non-atomic pointer read + separate clear with xchg() in both racing paths. xchg() is a single indivisible hardware instruction that atomically reads the old pointer and writes NULL. This guarantees only one of the two concurrent callers obtains the non-NULL pointer and performs the put, the other gets NULL and skips it. Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/15880 Fixes: 058179e72e09 ("drm/i915/gt: Replace hangcheck by heartbeats") Cc: # v5.5+ Signed-off-by: Sebastian Brzezinka Reviewed-by: Krzysztof Karas Reviewed-by: Andi Shyti Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/d4c1c14255688dd07cc8044973c4f032a8d1559e.1775038106.git.sebastian.brzezinka@intel.com (cherry picked from commit 13238dc0ee4f9ab8dafa2cca7295736191ae2f42) Signed-off-by: Joonas Lahtinen --- .../gpu/drm/i915/gt/intel_engine_heartbeat.c | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c index b279878dca2932..6424ecce8bcbea 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c @@ -148,10 +148,12 @@ static void heartbeat(struct work_struct *wrk) /* Just in case everything has gone horribly wrong, give it a kick */ intel_engine_flush_submission(engine); - rq = engine->heartbeat.systole; - if (rq && i915_request_completed(rq)) { - i915_request_put(rq); - engine->heartbeat.systole = NULL; + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) { + if (i915_request_completed(rq)) + i915_request_put(rq); + else + engine->heartbeat.systole = rq; } if (!intel_engine_pm_get_if_awake(engine)) @@ -232,8 +234,11 @@ static void heartbeat(struct work_struct *wrk) unlock: mutex_unlock(&ce->timeline->mutex); out: - if (!engine->i915->params.enable_hangcheck || !next_heartbeat(engine)) - i915_request_put(fetch_and_zero(&engine->heartbeat.systole)); + if (!engine->i915->params.enable_hangcheck || !next_heartbeat(engine)) { + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) + i915_request_put(rq); + } intel_engine_pm_put(engine); } @@ -247,8 +252,13 @@ void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine) void intel_engine_park_heartbeat(struct intel_engine_cs *engine) { - if (cancel_delayed_work(&engine->heartbeat.work)) - i915_request_put(fetch_and_zero(&engine->heartbeat.systole)); + if (cancel_delayed_work(&engine->heartbeat.work)) { + struct i915_request *rq; + + rq = xchg(&engine->heartbeat.systole, NULL); + if (rq) + i915_request_put(rq); + } } void intel_gt_unpark_heartbeats(struct intel_gt *gt) From 9a91797e61d286805ae10a92cc48959c30800556 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 1 Apr 2026 15:58:01 +0800 Subject: [PATCH 574/700] ipvs: fix NULL deref in ip_vs_add_service error path When ip_vs_bind_scheduler() succeeds in ip_vs_add_service(), the local variable sched is set to NULL. If ip_vs_start_estimator() subsequently fails, the out_err cleanup calls ip_vs_unbind_scheduler(svc, sched) with sched == NULL. ip_vs_unbind_scheduler() passes the cur_sched NULL check (because svc->scheduler was set by the successful bind) but then dereferences the NULL sched parameter at sched->done_service, causing a kernel panic at offset 0x30 from NULL. Oops: general protection fault, [..] [#1] PREEMPT SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] RIP: 0010:ip_vs_unbind_scheduler (net/netfilter/ipvs/ip_vs_sched.c:69) Call Trace: ip_vs_add_service.isra.0 (net/netfilter/ipvs/ip_vs_ctl.c:1500) do_ip_vs_set_ctl (net/netfilter/ipvs/ip_vs_ctl.c:2809) nf_setsockopt (net/netfilter/nf_sockopt.c:102) [..] Fix by simply not clearing the local sched variable after a successful bind. ip_vs_unbind_scheduler() already detects whether a scheduler is installed via svc->scheduler, and keeping sched non-NULL ensures the error path passes the correct pointer to both ip_vs_unbind_scheduler() and ip_vs_scheduler_put(). While the bug is older, the problem popups in more recent kernels (6.2), when the new error path is taken after the ip_vs_start_estimator() call. Fixes: 705dd3444081 ("ipvs: use kthreads for stats estimation") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Acked-by: Simon Horman Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_ctl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 35642de2a0fee4..2aaf50f52c8e8e 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1452,7 +1452,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ret = ip_vs_bind_scheduler(svc, sched); if (ret) goto out_err; - sched = NULL; } ret = ip_vs_start_estimator(ipvs, &svc->stats); From 1f3083aec8836213da441270cdb1ab612dd82cf4 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 1 Apr 2026 14:20:57 -0700 Subject: [PATCH 575/700] netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE terminator When batching multiple NFLOG messages (inst->qlen > 1), __nfulnl_send() appends an NLMSG_DONE terminator with sizeof(struct nfgenmsg) payload via nlmsg_put(), but never initializes the nfgenmsg bytes. The nlmsg_put() helper only zeroes alignment padding after the payload, not the payload itself, so four bytes of stale kernel heap data are leaked to userspace in the NLMSG_DONE message body. Use nfnl_msg_put() to build the NLMSG_DONE terminator, which initializes the nfgenmsg payload via nfnl_fill_hdr(), consistent with how __build_packet_message() already constructs NFULNL_MSG_PACKET headers. Fixes: 29c5d4afba51 ("[NETFILTER]: nfnetlink_log: fix sending of multipart messages") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal --- net/netfilter/nfnetlink_log.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index f80978c06fa0a0..0db908518b2fa2 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -361,10 +361,10 @@ static void __nfulnl_send(struct nfulnl_instance *inst) { if (inst->qlen > 1) { - struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0, - NLMSG_DONE, - sizeof(struct nfgenmsg), - 0); + struct nlmsghdr *nlh = nfnl_msg_put(inst->skb, 0, 0, + NLMSG_DONE, 0, + AF_UNSPEC, NFNETLINK_V0, + htons(inst->group_num)); if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n", inst->skb->len, skb_tailroom(inst->skb))) { kfree_skb(inst->skb); From ff64c5bfef12461df8450e0f50bb693b5269c720 Mon Sep 17 00:00:00 2001 From: Ren Wei Date: Fri, 3 Apr 2026 23:52:52 +0800 Subject: [PATCH 576/700] netfilter: xt_multiport: validate range encoding in checkentry ports_match_v1() treats any non-zero pflags entry as the start of a port range and unconditionally consumes the next ports[] element as the range end. The checkentry path currently validates protocol, flags and count, but it does not validate the range encoding itself. As a result, malformed rules can mark the last slot as a range start or place two range starts back to back, leaving ports_match_v1() to step past the last valid ports[] element while interpreting the rule. Reject malformed multiport v1 rules in checkentry by validating that each range start has a following element and that the following element is not itself marked as another range start. Fixes: a89ecb6a2ef7 ("[NETFILTER]: x_tables: unify IPv4/IPv6 multiport match") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Yuhang Zheng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal --- net/netfilter/xt_multiport.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index 44a00f5acde8ae..a1691ff405d3c4 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -105,6 +105,28 @@ multiport_mt(const struct sk_buff *skb, struct xt_action_param *par) return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1])); } +static bool +multiport_valid_ranges(const struct xt_multiport_v1 *multiinfo) +{ + unsigned int i; + + for (i = 0; i < multiinfo->count; i++) { + if (!multiinfo->pflags[i]) + continue; + + if (++i >= multiinfo->count) + return false; + + if (multiinfo->pflags[i]) + return false; + + if (multiinfo->ports[i - 1] > multiinfo->ports[i]) + return false; + } + + return true; +} + static inline bool check(u_int16_t proto, u_int8_t ip_invflags, @@ -127,8 +149,10 @@ static int multiport_mt_check(const struct xt_mtchk_param *par) const struct ipt_ip *ip = par->entryinfo; const struct xt_multiport_v1 *multiinfo = par->matchinfo; - return check(ip->proto, ip->invflags, multiinfo->flags, - multiinfo->count) ? 0 : -EINVAL; + if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count)) + return -EINVAL; + + return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL; } static int multiport_mt6_check(const struct xt_mtchk_param *par) @@ -136,8 +160,10 @@ static int multiport_mt6_check(const struct xt_mtchk_param *par) const struct ip6t_ip6 *ip = par->entryinfo; const struct xt_multiport_v1 *multiinfo = par->matchinfo; - return check(ip->proto, ip->invflags, multiinfo->flags, - multiinfo->count) ? 0 : -EINVAL; + if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count)) + return -EINVAL; + + return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL; } static struct xt_match multiport_mt_reg[] __read_mostly = { From fdce0b3590f724540795b874b4c8850c90e6b0a8 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Sat, 4 Apr 2026 17:39:47 +0800 Subject: [PATCH 577/700] netfilter: ip6t_eui64: reject invalid MAC header for all packets `eui64_mt6()` derives a modified EUI-64 from the Ethernet source address and compares it with the low 64 bits of the IPv6 source address. The existing guard only rejects an invalid MAC header when `par->fragoff != 0`. For packets with `par->fragoff == 0`, `eui64_mt6()` can still reach `eth_hdr(skb)` even when the MAC header is not valid. Fix this by removing the `par->fragoff != 0` condition so that packets with an invalid MAC header are rejected before accessing `eth_hdr(skb)`. Fixes: 1da177e4c3f41 ("Linux-2.6.12-rc2") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Signed-off-by: Florian Westphal --- net/ipv6/netfilter/ip6t_eui64.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index d704f7ed300c24..da69a27e8332c1 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -22,8 +22,7 @@ eui64_mt6(const struct sk_buff *skb, struct xt_action_param *par) unsigned char eui64[8]; if (!(skb_mac_header(skb) >= skb->head && - skb_mac_header(skb) + ETH_HLEN <= skb->data) && - par->fragoff != 0) { + skb_mac_header(skb) + ETH_HLEN <= skb->data)) { par->hotdrop = true; return false; } From f8dca15a1b190787bbd03285304b569631160eda Mon Sep 17 00:00:00 2001 From: Tuan Do Date: Fri, 3 Apr 2026 00:33:17 -0700 Subject: [PATCH 578/700] netfilter: nft_ct: fix use-after-free in timeout object destroy nft_ct_timeout_obj_destroy() frees the timeout object with kfree() immediately after nf_ct_untimeout(), without waiting for an RCU grace period. Concurrent packet processing on other CPUs may still hold RCU-protected references to the timeout object obtained via rcu_dereference() in nf_ct_timeout_data(). Add an rcu_head to struct nf_ct_timeout and use kfree_rcu() to defer freeing until after an RCU grace period, matching the approach already used in nfnetlink_cttimeout.c. KASAN report: BUG: KASAN: slab-use-after-free in nf_conntrack_tcp_packet+0x1381/0x29d0 Read of size 4 at addr ffff8881035fe19c by task exploit/80 Call Trace: nf_conntrack_tcp_packet+0x1381/0x29d0 nf_conntrack_in+0x612/0x8b0 nf_hook_slow+0x70/0x100 __ip_local_out+0x1b2/0x210 tcp_sendmsg_locked+0x722/0x1580 __sys_sendto+0x2d8/0x320 Allocated by task 75: nft_ct_timeout_obj_init+0xf6/0x290 nft_obj_init+0x107/0x1b0 nf_tables_newobj+0x680/0x9c0 nfnetlink_rcv_batch+0xc29/0xe00 Freed by task 26: nft_obj_destroy+0x3f/0xa0 nf_tables_trans_destroy_work+0x51c/0x5c0 process_one_work+0x2c4/0x5a0 Fixes: 7e0b2b57f01d ("netfilter: nft_ct: add ct timeout support") Cc: stable@vger.kernel.org Signed-off-by: Tuan Do Signed-off-by: Florian Westphal --- include/net/netfilter/nf_conntrack_timeout.h | 1 + net/netfilter/nft_ct.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 9fdaba911de64d..3a66d4abb6d688 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -14,6 +14,7 @@ struct nf_ct_timeout { __u16 l3num; const struct nf_conntrack_l4proto *l4proto; + struct rcu_head rcu; char data[]; }; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 128ff8155b5de1..04c74ccf9b848a 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1020,7 +1020,7 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx, nf_queue_nf_hook_drop(ctx->net); nf_ct_untimeout(ctx->net, timeout); nf_ct_netns_put(ctx->net, ctx->family); - kfree(priv->timeout); + kfree_rcu(priv->timeout, rcu); } static int nft_ct_timeout_obj_dump(struct sk_buff *skb, From 936206e3f6ff411581e615e930263d6f8b78df9d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 7 Apr 2026 17:00:01 +0200 Subject: [PATCH 579/700] netfilter: nfnetlink_queue: make hash table per queue Sharing a global hash table among all queues is tempting, but it can cause crash: BUG: KASAN: slab-use-after-free in nfqnl_recv_verdict+0x11ac/0x15e0 [nfnetlink_queue] [..] nfqnl_recv_verdict+0x11ac/0x15e0 [nfnetlink_queue] nfnetlink_rcv_msg+0x46a/0x930 kmem_cache_alloc_node_noprof+0x11e/0x450 struct nf_queue_entry is freed via kfree, but parallel cpu can still encounter such an nf_queue_entry when walking the list. Alternative fix is to free the nf_queue_entry via kfree_rcu() instead, but as we have to alloc/free for each skb this will cause more mem pressure. Cc: Scott Mitchell Fixes: e19079adcd26 ("netfilter: nfnetlink_queue: optimize verdict lookup with hash table") Signed-off-by: Florian Westphal --- include/net/netfilter/nf_queue.h | 1 - net/netfilter/nfnetlink_queue.c | 139 +++++++++++-------------------- 2 files changed, 49 insertions(+), 91 deletions(-) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 45eb26b2e95b37..d17035d14d96cf 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -23,7 +23,6 @@ struct nf_queue_entry { struct nf_hook_state state; bool nf_ct_is_unconfirmed; u16 size; /* sizeof(entry) + saved route keys */ - u16 queue_num; /* extra space to store route keys */ }; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 47f7f62906e218..8e02f84784dabc 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -49,8 +49,8 @@ #endif #define NFQNL_QMAX_DEFAULT 1024 -#define NFQNL_HASH_MIN 1024 -#define NFQNL_HASH_MAX 1048576 +#define NFQNL_HASH_MIN 8 +#define NFQNL_HASH_MAX 32768 /* We're using struct nlattr which has 16bit nla_len. Note that nla_len * includes the header length. Thus, the maximum packet length that we @@ -60,29 +60,10 @@ */ #define NFQNL_MAX_COPY_RANGE (0xffff - NLA_HDRLEN) -/* Composite key for packet lookup: (net, queue_num, packet_id) */ -struct nfqnl_packet_key { - possible_net_t net; - u32 packet_id; - u16 queue_num; -} __aligned(sizeof(u32)); /* jhash2 requires 32-bit alignment */ - -/* Global rhashtable - one for entire system, all netns */ -static struct rhashtable nfqnl_packet_map __read_mostly; - -/* Helper to initialize composite key */ -static inline void nfqnl_init_key(struct nfqnl_packet_key *key, - struct net *net, u32 packet_id, u16 queue_num) -{ - memset(key, 0, sizeof(*key)); - write_pnet(&key->net, net); - key->packet_id = packet_id; - key->queue_num = queue_num; -} - struct nfqnl_instance { struct hlist_node hlist; /* global list of queues */ - struct rcu_head rcu; + struct rhashtable nfqnl_packet_map; + struct rcu_work rwork; u32 peer_portid; unsigned int queue_maxlen; @@ -106,6 +87,7 @@ struct nfqnl_instance { typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); +static struct workqueue_struct *nfq_cleanup_wq __read_mostly; static unsigned int nfnl_queue_net_id __read_mostly; #define INSTANCE_BUCKETS 16 @@ -124,34 +106,10 @@ static inline u_int8_t instance_hashfn(u_int16_t queue_num) return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS; } -/* Extract composite key from nf_queue_entry for hashing */ -static u32 nfqnl_packet_obj_hashfn(const void *data, u32 len, u32 seed) -{ - const struct nf_queue_entry *entry = data; - struct nfqnl_packet_key key; - - nfqnl_init_key(&key, entry->state.net, entry->id, entry->queue_num); - - return jhash2((u32 *)&key, sizeof(key) / sizeof(u32), seed); -} - -/* Compare stack-allocated key against entry */ -static int nfqnl_packet_obj_cmpfn(struct rhashtable_compare_arg *arg, - const void *obj) -{ - const struct nfqnl_packet_key *key = arg->key; - const struct nf_queue_entry *entry = obj; - - return !net_eq(entry->state.net, read_pnet(&key->net)) || - entry->queue_num != key->queue_num || - entry->id != key->packet_id; -} - static const struct rhashtable_params nfqnl_rhashtable_params = { .head_offset = offsetof(struct nf_queue_entry, hash_node), - .key_len = sizeof(struct nfqnl_packet_key), - .obj_hashfn = nfqnl_packet_obj_hashfn, - .obj_cmpfn = nfqnl_packet_obj_cmpfn, + .key_offset = offsetof(struct nf_queue_entry, id), + .key_len = sizeof(u32), .automatic_shrinking = true, .min_size = NFQNL_HASH_MIN, .max_size = NFQNL_HASH_MAX, @@ -190,6 +148,10 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid) spin_lock_init(&inst->lock); INIT_LIST_HEAD(&inst->queue_list); + err = rhashtable_init(&inst->nfqnl_packet_map, &nfqnl_rhashtable_params); + if (err < 0) + goto out_free; + spin_lock(&q->instances_lock); if (instance_lookup(q, queue_num)) { err = -EEXIST; @@ -210,6 +172,8 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid) out_unlock: spin_unlock(&q->instances_lock); + rhashtable_destroy(&inst->nfqnl_packet_map); +out_free: kfree(inst); return ERR_PTR(err); } @@ -217,15 +181,18 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid) static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data); -static void -instance_destroy_rcu(struct rcu_head *head) +static void instance_destroy_work(struct work_struct *work) { - struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, - rcu); + struct nfqnl_instance *inst; + inst = container_of(to_rcu_work(work), struct nfqnl_instance, + rwork); rcu_read_lock(); nfqnl_flush(inst, NULL, 0); rcu_read_unlock(); + + rhashtable_destroy(&inst->nfqnl_packet_map); + kfree(inst); module_put(THIS_MODULE); } @@ -234,7 +201,9 @@ static void __instance_destroy(struct nfqnl_instance *inst) { hlist_del_rcu(&inst->hlist); - call_rcu(&inst->rcu, instance_destroy_rcu); + + INIT_RCU_WORK(&inst->rwork, instance_destroy_work); + queue_rcu_work(nfq_cleanup_wq, &inst->rwork); } static void @@ -250,9 +219,7 @@ __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) { int err; - entry->queue_num = queue->queue_num; - - err = rhashtable_insert_fast(&nfqnl_packet_map, &entry->hash_node, + err = rhashtable_insert_fast(&queue->nfqnl_packet_map, &entry->hash_node, nfqnl_rhashtable_params); if (unlikely(err)) return err; @@ -266,23 +233,19 @@ __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) static void __dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) { - rhashtable_remove_fast(&nfqnl_packet_map, &entry->hash_node, + rhashtable_remove_fast(&queue->nfqnl_packet_map, &entry->hash_node, nfqnl_rhashtable_params); list_del(&entry->list); queue->queue_total--; } static struct nf_queue_entry * -find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id, - struct net *net) +find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) { - struct nfqnl_packet_key key; struct nf_queue_entry *entry; - nfqnl_init_key(&key, net, id, queue->queue_num); - spin_lock_bh(&queue->lock); - entry = rhashtable_lookup_fast(&nfqnl_packet_map, &key, + entry = rhashtable_lookup_fast(&queue->nfqnl_packet_map, &id, nfqnl_rhashtable_params); if (entry) @@ -1531,7 +1494,7 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info, verdict = ntohl(vhdr->verdict); - entry = find_dequeue_entry(queue, ntohl(vhdr->id), info->net); + entry = find_dequeue_entry(queue, ntohl(vhdr->id)); if (entry == NULL) return -ENOENT; @@ -1880,40 +1843,38 @@ static int __init nfnetlink_queue_init(void) { int status; - status = rhashtable_init(&nfqnl_packet_map, &nfqnl_rhashtable_params); - if (status < 0) - return status; + nfq_cleanup_wq = alloc_ordered_workqueue("nfq_workqueue", 0); + if (!nfq_cleanup_wq) + return -ENOMEM; status = register_pernet_subsys(&nfnl_queue_net_ops); - if (status < 0) { - pr_err("failed to register pernet ops\n"); - goto cleanup_rhashtable; - } + if (status < 0) + goto cleanup_pernet_subsys; - netlink_register_notifier(&nfqnl_rtnl_notifier); - status = nfnetlink_subsys_register(&nfqnl_subsys); - if (status < 0) { - pr_err("failed to create netlink socket\n"); - goto cleanup_netlink_notifier; - } + status = netlink_register_notifier(&nfqnl_rtnl_notifier); + if (status < 0) + goto cleanup_rtnl_notifier; status = register_netdevice_notifier(&nfqnl_dev_notifier); - if (status < 0) { - pr_err("failed to register netdevice notifier\n"); - goto cleanup_netlink_subsys; - } + if (status < 0) + goto cleanup_dev_notifier; + + status = nfnetlink_subsys_register(&nfqnl_subsys); + if (status < 0) + goto cleanup_nfqnl_subsys; nf_register_queue_handler(&nfqh); return status; -cleanup_netlink_subsys: - nfnetlink_subsys_unregister(&nfqnl_subsys); -cleanup_netlink_notifier: +cleanup_nfqnl_subsys: + unregister_netdevice_notifier(&nfqnl_dev_notifier); +cleanup_dev_notifier: netlink_unregister_notifier(&nfqnl_rtnl_notifier); +cleanup_rtnl_notifier: unregister_pernet_subsys(&nfnl_queue_net_ops); -cleanup_rhashtable: - rhashtable_destroy(&nfqnl_packet_map); +cleanup_pernet_subsys: + destroy_workqueue(nfq_cleanup_wq); return status; } @@ -1924,9 +1885,7 @@ static void __exit nfnetlink_queue_fini(void) nfnetlink_subsys_unregister(&nfqnl_subsys); netlink_unregister_notifier(&nfqnl_rtnl_notifier); unregister_pernet_subsys(&nfnl_queue_net_ops); - - rhashtable_destroy(&nfqnl_packet_map); - + destroy_workqueue(nfq_cleanup_wq); rcu_barrier(); /* Wait for completion of call_rcu()'s */ } From dde1a6084c5ca9d143a562540d5453454d79ea15 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 6 Apr 2026 23:18:31 +0200 Subject: [PATCH 580/700] selftests: nft_queue.sh: add a parallel stress test Introduce a new stress test to check for race conditions in the nfnetlink_queue subsystem, where an entry is freed while another CPU is concurrently walking the global rhashtable. To trigger this, `nf_queue.c` is extended with two new flags: * -O (out-of-order): Buffers packet IDs and flushes them in reverse. * -b (bogus verdicts): Floods the kernel with non-existent packet IDs. The bogus verdict loop forces the kernel's lookup function to perform full rhashtable bucket traversals (-ENOENT). Combined with reverse-order flushing and heavy parallel UDP/ping flooding across 8 queues, this puts the nfnetlink_queue code under pressure. Joint work with Florian Westphal. Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal --- .../selftests/net/netfilter/nf_queue.c | 50 +++++++++-- .../selftests/net/netfilter/nft_queue.sh | 83 ++++++++++++++++--- 2 files changed, 115 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/net/netfilter/nf_queue.c b/tools/testing/selftests/net/netfilter/nf_queue.c index 116c0ca0eabbe9..8bbec37f535630 100644 --- a/tools/testing/selftests/net/netfilter/nf_queue.c +++ b/tools/testing/selftests/net/netfilter/nf_queue.c @@ -19,6 +19,8 @@ struct options { bool count_packets; bool gso_enabled; bool failopen; + bool out_of_order; + bool bogus_verdict; int verbose; unsigned int queue_num; unsigned int timeout; @@ -31,7 +33,7 @@ static struct options opts; static void help(const char *p) { - printf("Usage: %s [-c|-v [-vv] ] [-o] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p); + printf("Usage: %s [-c|-v [-vv] ] [-o] [-O] [-b] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p); } static int parse_attr_cb(const struct nlattr *attr, void *data) @@ -275,7 +277,9 @@ static int mainloop(void) unsigned int buflen = 64 * 1024 + MNL_SOCKET_BUFFER_SIZE; struct mnl_socket *nl; struct nlmsghdr *nlh; + uint32_t ooo_ids[16]; unsigned int portid; + int ooo_count = 0; char *buf; int ret; @@ -308,6 +312,9 @@ static int mainloop(void) ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL); if (ret < 0) { + /* bogus verdict mode will generate ENOENT error messages */ + if (opts.bogus_verdict && errno == ENOENT) + continue; perror("mnl_cb_run"); exit(EXIT_FAILURE); } @@ -316,10 +323,35 @@ static int mainloop(void) if (opts.delay_ms) sleep_ms(opts.delay_ms); - nlh = nfq_build_verdict(buf, id, opts.queue_num, opts.verdict); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_sendto"); - exit(EXIT_FAILURE); + if (opts.bogus_verdict) { + for (int i = 0; i < 50; i++) { + nlh = nfq_build_verdict(buf, id + 0x7FFFFFFF + i, + opts.queue_num, opts.verdict); + mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + } + } + + if (opts.out_of_order) { + ooo_ids[ooo_count] = id; + if (ooo_count >= 15) { + for (ooo_count; ooo_count >= 0; ooo_count--) { + nlh = nfq_build_verdict(buf, ooo_ids[ooo_count], + opts.queue_num, opts.verdict); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + } + ooo_count = 0; + } else { + ooo_count++; + } + } else { + nlh = nfq_build_verdict(buf, id, opts.queue_num, opts.verdict); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } } } @@ -332,7 +364,7 @@ static void parse_opts(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "chvot:q:Q:d:G")) != -1) { + while ((c = getopt(argc, argv, "chvoObt:q:Q:d:G")) != -1) { switch (c) { case 'c': opts.count_packets = true; @@ -375,6 +407,12 @@ static void parse_opts(int argc, char **argv) case 'v': opts.verbose++; break; + case 'O': + opts.out_of_order = true; + break; + case 'b': + opts.bogus_verdict = true; + break; } } diff --git a/tools/testing/selftests/net/netfilter/nft_queue.sh b/tools/testing/selftests/net/netfilter/nft_queue.sh index ea766bdc5d04d1..d80390848e8509 100755 --- a/tools/testing/selftests/net/netfilter/nft_queue.sh +++ b/tools/testing/selftests/net/netfilter/nft_queue.sh @@ -11,6 +11,7 @@ ret=0 timeout=5 SCTP_TEST_TIMEOUT=60 +STRESS_TEST_TIMEOUT=30 cleanup() { @@ -719,6 +720,74 @@ EOF fi } +check_tainted() +{ + local msg="$1" + + if [ "$tainted_then" -ne 0 ];then + return + fi + + read tainted_now < /proc/sys/kernel/tainted + if [ "$tainted_now" -eq 0 ];then + echo "PASS: $msg" + else + echo "TAINT: $msg" + dmesg + ret=1 + fi +} + +test_queue_stress() +{ + read tainted_then < /proc/sys/kernel/tainted + local i + + ip netns exec "$nsrouter" nft -f /dev/stdin < /dev/null & + + timeout "$STRESS_TEST_TIMEOUT" ip netns exec "$ns3" \ + socat -u UDP-LISTEN:12345,fork,pf=ipv4 STDOUT > /dev/null & + + for i in $(seq 0 7); do + ip netns exec "$nsrouter" timeout "$STRESS_TEST_TIMEOUT" \ + ./nf_queue -q $i -t 2 -O -b > /dev/null & + done + + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + ping -q -f 10.0.2.99 > /dev/null 2>&1 & + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + ping -q -f 10.0.3.99 > /dev/null 2>&1 & + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + ping -q -f "dead:2::99" > /dev/null 2>&1 & + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + ping -q -f "dead:3::99" > /dev/null 2>&1 & + + busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns2" 12345 + busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns3" 12345 + + for i in $(seq 1 4);do + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + socat -u STDIN UDP-DATAGRAM:10.0.2.99:12345 < /dev/zero > /dev/null & + ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \ + socat -u STDIN UDP-DATAGRAM:10.0.3.99:12345 < /dev/zero > /dev/null & + done + + wait + + check_tainted "concurrent queueing" +} + test_queue_removal() { read tainted_then < /proc/sys/kernel/tainted @@ -742,18 +811,7 @@ EOF ip netns exec "$ns1" nft flush ruleset - if [ "$tainted_then" -ne 0 ];then - return - fi - - read tainted_now < /proc/sys/kernel/tainted - if [ "$tainted_now" -eq 0 ];then - echo "PASS: queue program exiting while packets queued" - else - echo "TAINT: queue program exiting while packets queued" - dmesg - ret=1 - fi + check_tainted "queue program exiting while packets queued" } ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null @@ -799,6 +857,7 @@ test_sctp_forward test_sctp_output test_udp_nat_race test_udp_gro_ct +test_queue_stress # should be last, adds vrf device in ns1 and changes routes test_icmp_vrf From f4c90fb761f696ebbcf1e8fb7f83d83cbb30cb0c Mon Sep 17 00:00:00 2001 From: Neo Chang Date: Wed, 8 Apr 2026 13:26:39 +0800 Subject: [PATCH 581/700] ASoC: nau8325: Add software reset during probe Currently, the driver only performs a hardware reset during the I2C probe sequence. To ensure all internal states of the codec are properly cleared without affecting the configuration registers, a software reset is also required. According to the hardware specification, writing to the Software Reset register (R01) twice will reset all internal states safely. This patch adds the nau8325_software_reset() function, executes it right after the hardware reset in the probe function, and marks the R01 register as writeable in the regmap configuration. Signed-off-by: Neo Chang Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260408052639.187149-1-YLCHANG2@nuvoton.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8325.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c index e651263a981217..58ef5c493835b5 100644 --- a/sound/soc/codecs/nau8325.c +++ b/sound/soc/codecs/nau8325.c @@ -142,7 +142,7 @@ static bool nau8325_readable_reg(struct device *dev, unsigned int reg) static bool nau8325_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case NAU8325_R00_HARDWARE_RST: + case NAU8325_R00_HARDWARE_RST ... NAU8325_R01_SOFTWARE_RST: case NAU8325_R03_CLK_CTRL ... NAU8325_R06_INT_CLR_STATUS: case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME: case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2: @@ -670,6 +670,12 @@ static void nau8325_reset_chip(struct regmap *regmap) regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0000); } +static void nau8325_software_reset(struct regmap *regmap) +{ + regmap_write(regmap, NAU8325_R01_SOFTWARE_RST, 0x0000); + regmap_write(regmap, NAU8325_R01_SOFTWARE_RST, 0x0000); +} + static void nau8325_init_regs(struct nau8325 *nau8325) { struct regmap *regmap = nau8325->regmap; @@ -856,6 +862,7 @@ static int nau8325_i2c_probe(struct i2c_client *i2c) nau8325_print_device_properties(nau8325); nau8325_reset_chip(nau8325->regmap); + nau8325_software_reset(nau8325->regmap); ret = regmap_read(nau8325->regmap, NAU8325_R02_DEVICE_ID, &value); if (ret) { dev_dbg(dev, "Failed to read device id (%d)", ret); From 082c192c0dd03f685514c9ce2eb0a80fd28e2175 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 8 Apr 2026 11:45:13 +0300 Subject: [PATCH 582/700] ALSA: hda/intel: enforce stricter period-size alignment for Intel NVL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intel ACE4 based products set more strict constraints on HDA BDLE start address and length alignment. Modify capability flags to drop AZX_DCAPS_NO_ALIGN_BUFSIZE for Intel Nova Lake platforms. Fixes: 7f428282fde3 ("ALSA: hda: controllers: intel: add support for Nova Lake") Signed-off-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Cc: Link: https://patch.msgid.link/20260408084514.24325-2-peter.ujfalusi@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 8a7bd49e411f7f..a4104376523ad4 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -295,6 +295,9 @@ enum { #define AZX_DCAPS_INTEL_LNL \ (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS) +#define AZX_DCAPS_INTEL_NVL \ + (AZX_DCAPS_INTEL_LNL & ~AZX_DCAPS_NO_ALIGN_BUFSIZE) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\ @@ -2565,8 +2568,8 @@ static const struct pci_device_id azx_ids[] = { /* Wildcat Lake */ { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Nova Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, - { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, + { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_NVL) }, + { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_NVL) }, /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ From 0f71866057262d669ed6a21246eaac0ad6d04d4e Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 8 Apr 2026 11:45:14 +0300 Subject: [PATCH 583/700] ASoC: SOF: Intel: hda: modify period size constraints for ACE4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intel ACE4 based products set more strict constraints on HDA BDLE start address and length alignment. Add a constraint to align period size to 128 bytes. The commit removes the "minimum as per HDA spec" comment. This comment was misleading as spec actually does allow a 2 byte BDLE length, and more importantly, period size also directly impacts how the BDLE start addresses are aligned, so it is not sufficient just to consider allowed buffer length. Fixes: d3df422f66e8 ("ASoC: SOF: Intel: add initial support for NVL-S") Cc: stable@vger.kernel.org Reported-by: Peter Ujfalusi Signed-off-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260408084514.24325-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-pcm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index da6c1e7263cde1..16a3640728210b 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -219,6 +219,7 @@ EXPORT_SYMBOL_NS(hda_dsp_pcm_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { + const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *scomp = sdev->component; @@ -268,8 +269,17 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, return -ENODEV; } - /* minimum as per HDA spec */ - snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); + /* + * Set period size constraint to ensure BDLE buffer length and + * start address alignment requirements are met. Align to 128 + * bytes for newer Intel platforms, with older ones using 4 byte alignment. + */ + if (chip_info->hw_ip_version >= SOF_INTEL_ACE_4_0) + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + else + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); /* avoid circular buffer wrap in middle of period */ snd_pcm_hw_constraint_integer(substream->runtime, From 0669631dbccd41cf3ca7aa70213fcd8bb41c4b38 Mon Sep 17 00:00:00 2001 From: Tomasz Merta Date: Wed, 8 Apr 2026 10:40:56 +0200 Subject: [PATCH 584/700] ASoC: stm32_sai: fix incorrect BCLK polarity for DSP_A/B, LEFT_J The STM32 SAI driver do not set the clock strobing bit (CKSTR) for DSP_A, DSP_B and LEFT_J formats, causing data to be sampled on the wrong BCLK edge when SND_SOC_DAIFMT_NB_NF is used. Per ALSA convention, NB_NF requires sampling on the rising BCLK edge. The STM32MP25 SAI reference manual states that CKSTR=1 is required for signals received by the SAI to be sampled on the SCK rising edge. Without setting CKSTR=1, the SAI samples on the falling edge, violating the NB_NF convention. For comparison, the NXP FSL SAI driver correctly sets FSL_SAI_CR2_BCP for DSP_A, DSP_B and LEFT_J, consistent with its I2S handling. This patch adds SAI_XCR1_CKSTR for DSP_A, DSP_B and LEFT_J in stm32_sai_set_dai_fmt which was verified empirically with a cs47l35 codec. RIGHT_J (LSB) is not investigated and addressed by this patch. Note: the STM32 I2S driver (stm32_i2s_set_dai_fmt) may have the same issue for DSP_A mode, as I2S_CGFR_CKPOL is not set. This has not been verified and is left for a separate investigation. Signed-off-by: Tomasz Merta Link: https://patch.msgid.link/20260408084056.20588-1-tommerta@gmail.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 450e1585edeee0..3e82fa90e719af 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -802,6 +802,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) break; /* Left justified */ case SND_SOC_DAIFMT_MSB: + cr1 |= SAI_XCR1_CKSTR; frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; break; /* Right justified */ @@ -809,9 +810,11 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; break; case SND_SOC_DAIFMT_DSP_A: + cr1 |= SAI_XCR1_CKSTR; frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF; break; case SND_SOC_DAIFMT_DSP_B: + cr1 |= SAI_XCR1_CKSTR; frcr |= SAI_XFRCR_FSPOL; break; default: From 3f4aa994c0cc3616b8758345508ccddc6f324dfc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 8 Apr 2026 05:22:06 +0000 Subject: [PATCH 585/700] ASoC: amd: acp-da7219-max98357a: tidyup acp_soc_is_rltk_max() acp-da7219-max98357a() user exists behind it. No need to has pre-define. Remove it. And it is local function, add static. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h5pmvxfm.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-da7219-max98357a.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 3c08e6e17e41cb..af559653e62554 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -47,8 +47,6 @@ static struct clk *da7219_dai_bclk; static struct clk *rt5682_dai_wclk; static struct clk *rt5682_dai_bclk; -void *acp_soc_is_rltk_max(struct device *dev); - static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { int ret; @@ -703,7 +701,7 @@ static struct snd_soc_card cz_rt5682_card = { .num_controls = ARRAY_SIZE(cz_mc_controls), }; -void *acp_soc_is_rltk_max(struct device *dev) +static void *acp_soc_is_rltk_max(struct device *dev) { const struct acpi_device_id *match; From 5241c2ca33bb181bf7abb7cb4bba1cc67d1b6278 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 2 Apr 2026 11:09:16 +0200 Subject: [PATCH 586/700] firmware: efi: Never declare sysfb_primary_display on x86 The x86 architecture comes with its own instance of the global state variable sysfb_primary_display. Never declare it in the EFI subsystem. Fix the test for CONFIG_FIRMWARE_EDID accordingly. Signed-off-by: Thomas Zimmermann Fixes: e65ca1646311 ("efi: export sysfb_primary_display for EDID") Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 5a595d026f58e0..6103b1a082d247 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -60,7 +60,7 @@ extern __weak const efi_config_table_type_t efi_arch_tables[]; * x86 defines its own instance of sysfb_primary_display and uses * it even without EFI, everything else can get them from here. */ -#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)) || defined(CONFIG_FIRMWARE_EDID) +#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_FIRMWARE_EDID)) struct sysfb_display_info sysfb_primary_display __section(".data"); EXPORT_SYMBOL_GPL(sysfb_primary_display); #endif From 23e0cbe55736de222ed975863cf06baf29bee5fe Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Wed, 8 Apr 2026 10:38:31 +0100 Subject: [PATCH 587/700] ASoC: SDCA: Fix overwritten var within for loop mask variable should not be overwritten within the for loop or it will skip certain bits. Change to using BIT() macro. Fixes: b9ab3b618241 ("ASoC: SDCA: Add some initial IRQ handlers") Signed-off-by: Maciej Strozek Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260408093835.2881486-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 1838dabcdf6047..9acb0be84674c8 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -117,9 +117,7 @@ static irqreturn_t function_status_handler(int irq, void *data) status = val; for_each_set_bit(mask, &status, BITS_PER_BYTE) { - mask = 1 << mask; - - switch (mask) { + switch (BIT(mask)) { case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION: //FIXME: Add init writes break; From 58dec4fac4c46b42b8264f2d8aa6972ff951c18b Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Wed, 8 Apr 2026 10:38:32 +0100 Subject: [PATCH 588/700] ASoC: SDCA: mask Function_Status value According to the SDCA specification [1], when writing Function_Status during handling this control, the value should mask off bit 7. [1] MIPI Specification for SoundWire Device Class for Audio, version 1.1, section 7.14.1.3 (Host Software Handling of Function_Status) Signed-off-by: Maciej Strozek Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260408093835.2881486-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 9acb0be84674c8..0693209ffed1b0 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -138,7 +138,7 @@ static irqreturn_t function_status_handler(int irq, void *data) } } - ret = regmap_write(interrupt->function_regmap, reg, val); + ret = regmap_write(interrupt->function_regmap, reg, val & 0x7F); if (ret < 0) { dev_err(dev, "failed to clear function status: %d\n", ret); goto error; From 0b8757b220f94421bd4ff50cce03886387c4e71c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 8 Apr 2026 10:38:34 +0100 Subject: [PATCH 589/700] ASoC: SDCA: Unregister IRQ handlers on module remove Ensure that all interrupt handlers are unregistered before the parent regmap_irq is unregistered. sdca_irq_cleanup() was only called from the component_remove(). If the module was loaded and removed without ever being component probed the FDL interrupts would not be unregistered and this would hit a WARN when devm called regmap_del_irq_chip() during the removal of the parent IRQ. Fixes: 4e53116437e9 ("ASoC: SDCA: Fix errors in IRQ cleanup") Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260408093835.2881486-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 4 ++-- sound/soc/sdca/sdca_class_function.c | 10 +++++++++- sound/soc/sdca/sdca_interrupts.c | 7 +++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index b47003c3d26ef4..a515cc3df09717 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -83,8 +83,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *function_regmap, int sdca_irq_populate(struct sdca_function_data *function, struct snd_soc_component *component, struct sdca_interrupt_info *info); -void sdca_irq_cleanup(struct sdca_function_data *function, - struct snd_soc_component *component, +void sdca_irq_cleanup(struct device *dev, + struct sdca_function_data *function, struct sdca_interrupt_info *info); struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, struct regmap *regmap, int irq); diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index fe404d769c78c8..730103120514b4 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -203,7 +203,7 @@ static void class_function_component_remove(struct snd_soc_component *component) struct class_function_drv *drv = snd_soc_component_get_drvdata(component); struct sdca_class_drv *core = drv->core; - sdca_irq_cleanup(drv->function, component, core->irq_info); + sdca_irq_cleanup(component->dev, drv->function, core->irq_info); } static int class_function_set_jack(struct snd_soc_component *component, @@ -411,6 +411,13 @@ static int class_function_probe(struct auxiliary_device *auxdev, return 0; } +static void class_function_remove(struct auxiliary_device *auxdev) +{ + struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); + + sdca_irq_cleanup(drv->dev, drv->function, drv->core->irq_info); +} + static int class_function_runtime_suspend(struct device *dev) { struct auxiliary_device *auxdev = to_auxiliary_dev(dev); @@ -559,6 +566,7 @@ static struct auxiliary_driver class_function_drv = { }, .probe = class_function_probe, + .remove = class_function_remove, .id_table = class_function_id_table }; module_auxiliary_driver(class_function_drv); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 0693209ffed1b0..5cdabf8ae9da37 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -555,17 +555,16 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); /** * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function + * @sdev: Device pointer against which the sdca_interrupt_info was allocated. * @function: Pointer to the SDCA Function. - * @component: Pointer to the ASoC component for the Function. * @info: Pointer to the SDCA interrupt info for this device. * * Typically this would be called from the driver for a single SDCA Function. */ -void sdca_irq_cleanup(struct sdca_function_data *function, - struct snd_soc_component *component, +void sdca_irq_cleanup(struct device *dev, + struct sdca_function_data *function, struct sdca_interrupt_info *info) { - struct device *dev = component->dev; int i; guard(mutex)(&info->irq_lock); From 6b6f7263d626886a96fce6352f94dfab7a24c339 Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Wed, 8 Apr 2026 19:00:06 +0530 Subject: [PATCH 590/700] ASoC: amd: acp: update DMI quirk and add ACP DMIC for Lenovo platforms Replace DMI_EXACT_MATCH with DMI_MATCH for Lenovo SKU entries (21YW, 21YX) so the quirk applies to all variants of these models, not just exact SKU matches. Add ASOC_SDW_ACP_DMIC flag alongside ASOC_SDW_CODEC_SPKR in driver_data for these Lenovo platform entries, as these platforms use ACP PDM DMIC instead of SoundWire DMIC for digital microphone support. Fixes: 3acf517e1ae0 ("ASoC: amd: amd_sdw: add machine driver quirk for Lenovo models") Tested-by: Mark Pearson Reviewed-by: Mark Pearson Signed-off-by: Syed Saba Kareem Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260408133029.1368317-1-syed.sabakareem@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 92749dc17df86e..cd7b1acc7216d0 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -99,17 +99,17 @@ static const struct dmi_system_id soc_sdw_quirk_table[] = { .callback = soc_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "21YW"), + DMI_MATCH(DMI_PRODUCT_SKU, "21YW"), }, - .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + .driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)), }, { .callback = soc_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "21YX"), + DMI_MATCH(DMI_PRODUCT_SKU, "21YX"), }, - .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + .driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)), }, { .callback = soc_sdw_quirk_cb, From ff14dafde15c11403fac61367a34fea08926e9ee Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 7 Apr 2026 22:16:27 -0700 Subject: [PATCH 591/700] Input: uinput - take event lock when submitting FF request "event" To avoid racing with FF playback events and corrupting device's event queue take event_lock spinlock when calling uinput_dev_event() when submitting a FF upload or erase "event". Tested-by: Mikhail Gavrilov Link: https://patch.msgid.link/adXkf6MWzlB8LA_s@google.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index a973e82205b5c8..0e9544a98e672a 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -25,8 +25,10 @@ #include #include #include +#include #include #include +#include #include #include "../input-compat.h" @@ -76,6 +78,8 @@ static int uinput_dev_event(struct input_dev *dev, struct uinput_device *udev = input_get_drvdata(dev); struct timespec64 ts; + lockdep_assert_held(&dev->event_lock); + ktime_get_ts64(&ts); udev->buff[udev->head] = (struct input_event) { @@ -147,6 +151,7 @@ static void uinput_request_release_slot(struct uinput_device *udev, static int uinput_request_send(struct uinput_device *udev, struct uinput_request *request) { + unsigned long flags; int retval = 0; spin_lock(&udev->state_lock); @@ -160,7 +165,9 @@ static int uinput_request_send(struct uinput_device *udev, * Tell our userspace application about this new request * by queueing an input event. */ + spin_lock_irqsave(&udev->dev->event_lock, flags); uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); + spin_unlock_irqrestore(&udev->dev->event_lock, flags); out: spin_unlock(&udev->state_lock); From 9a52d1b7cb4a00a0dde3036b56a9706f54fccbb9 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Mon, 6 Apr 2026 18:31:31 +0800 Subject: [PATCH 592/700] ASoC: tas2781: Explicit association of Device, Device Name, and Device ID By correlating devices with their names and IDs, the driver becomes more discoverable. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260406103131.1883-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 104 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 41b89fcc69c370..c593f9da0c5b9b 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -121,29 +121,28 @@ static const struct i2c_device_id tasdevice_id[] = { { "tas5830", TAS5830 }, {} }; -MODULE_DEVICE_TABLE(i2c, tasdevice_id); #ifdef CONFIG_OF static const struct of_device_id tasdevice_of_match[] = { - { .compatible = "ti,tas2020" }, - { .compatible = "ti,tas2118" }, - { .compatible = "ti,tas2120" }, - { .compatible = "ti,tas2320" }, - { .compatible = "ti,tas2563" }, - { .compatible = "ti,tas2568" }, - { .compatible = "ti,tas2570" }, - { .compatible = "ti,tas2572" }, - { .compatible = "ti,tas2574" }, - { .compatible = "ti,tas2781" }, - { .compatible = "ti,tas5802" }, - { .compatible = "ti,tas5806m" }, - { .compatible = "ti,tas5806md" }, - { .compatible = "ti,tas5815" }, - { .compatible = "ti,tas5822" }, - { .compatible = "ti,tas5825" }, - { .compatible = "ti,tas5827" }, - { .compatible = "ti,tas5828" }, - { .compatible = "ti,tas5830" }, + { .compatible = "ti,tas2020", .data = &tasdevice_id[TAS2020] }, + { .compatible = "ti,tas2118", .data = &tasdevice_id[TAS2118] }, + { .compatible = "ti,tas2120", .data = &tasdevice_id[TAS2120] }, + { .compatible = "ti,tas2320", .data = &tasdevice_id[TAS2320] }, + { .compatible = "ti,tas2563", .data = &tasdevice_id[TAS2563] }, + { .compatible = "ti,tas2568", .data = &tasdevice_id[TAS2568] }, + { .compatible = "ti,tas2570", .data = &tasdevice_id[TAS2570] }, + { .compatible = "ti,tas2572", .data = &tasdevice_id[TAS2572] }, + { .compatible = "ti,tas2574", .data = &tasdevice_id[TAS2574] }, + { .compatible = "ti,tas2781", .data = &tasdevice_id[TAS2781] }, + { .compatible = "ti,tas5802", .data = &tasdevice_id[TAS5802] }, + { .compatible = "ti,tas5806m", .data = &tasdevice_id[TAS5806M] }, + { .compatible = "ti,tas5806md", .data = &tasdevice_id[TAS5806MD] }, + { .compatible = "ti,tas5815", .data = &tasdevice_id[TAS5815] }, + { .compatible = "ti,tas5822", .data = &tasdevice_id[TAS5822] }, + { .compatible = "ti,tas5825", .data = &tasdevice_id[TAS5825] }, + { .compatible = "ti,tas5827", .data = &tasdevice_id[TAS5827] }, + { .compatible = "ti,tas5828", .data = &tasdevice_id[TAS5828] }, + { .compatible = "ti,tas5830", .data = &tasdevice_id[TAS5830] }, {}, }; MODULE_DEVICE_TABLE(of, tasdevice_of_match); @@ -2023,15 +2022,12 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) if (IS_ERR(tas_priv->reset)) dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", __func__); - - strscpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name, - sizeof(tas_priv->dev_name)); } static int tasdevice_i2c_probe(struct i2c_client *i2c) { - const struct acpi_device_id *acpi_id; struct tasdevice_priv *tas_priv; + struct i2c_device_id *id_data; int ret; tas_priv = tasdevice_kzalloc(i2c); @@ -2041,20 +2037,23 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) dev_set_drvdata(&i2c->dev, tas_priv); if (ACPI_HANDLE(&i2c->dev)) { - acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, - &i2c->dev); - if (!acpi_id) { - dev_err(&i2c->dev, "No driver data\n"); - ret = -EINVAL; - goto err; - } - tas_priv->chip_id = acpi_id->driver_data; + id_data = (struct i2c_device_id *) + acpi_device_get_match_data(&i2c->dev); tas_priv->isacpi = true; } else { - tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c); + id_data = (struct i2c_device_id *)i2c_get_match_data(i2c); tas_priv->isacpi = false; } + if (!id_data) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + + tas_priv->chip_id = (uintptr_t)id_data->driver_data; + strscpy(tas_priv->dev_name, id_data->name, sizeof(tas_priv->dev_name)); + tasdevice_parse_dt(tas_priv); ret = tasdevice_init(tas_priv); @@ -2086,25 +2085,25 @@ static void tasdevice_i2c_remove(struct i2c_client *client) #ifdef CONFIG_ACPI static const struct acpi_device_id tasdevice_acpi_match[] = { - { "TXNW2020", TAS2020 }, - { "TXNW2118", TAS2118 }, - { "TXNW2120", TAS2120 }, - { "TXNW2320", TAS2320 }, - { "TXNW2563", TAS2563 }, - { "TXNW2568", TAS2568 }, - { "TXNW2570", TAS2570 }, - { "TXNW2572", TAS2572 }, - { "TXNW2574", TAS2574 }, - { "TXNW2781", TAS2781 }, - { "TXNW5802", TAS5802 }, - { "TXNW806M", TAS5806M }, - { "TXNW806D", TAS5806MD }, - { "TXNW5815", TAS5815 }, - { "TXNW5822", TAS5822 }, - { "TXNW5825", TAS5825 }, - { "TXNW5827", TAS5827 }, - { "TXNW5828", TAS5828 }, - { "TXNW5830", TAS5830 }, + { "TXNW2020", (kernel_ulong_t)&tasdevice_id[TAS2020] }, + { "TXNW2118", (kernel_ulong_t)&tasdevice_id[TAS2118] }, + { "TXNW2120", (kernel_ulong_t)&tasdevice_id[TAS2120] }, + { "TXNW2320", (kernel_ulong_t)&tasdevice_id[TAS2320] }, + { "TXNW2563", (kernel_ulong_t)&tasdevice_id[TAS2563] }, + { "TXNW2568", (kernel_ulong_t)&tasdevice_id[TAS2568] }, + { "TXNW2570", (kernel_ulong_t)&tasdevice_id[TAS2570] }, + { "TXNW2572", (kernel_ulong_t)&tasdevice_id[TAS2572] }, + { "TXNW2574", (kernel_ulong_t)&tasdevice_id[TAS2574] }, + { "TXNW2781", (kernel_ulong_t)&tasdevice_id[TAS2781] }, + { "TXNW5802", (kernel_ulong_t)&tasdevice_id[TAS5802] }, + { "TXNW806M", (kernel_ulong_t)&tasdevice_id[TAS5806M] }, + { "TXNW806D", (kernel_ulong_t)&tasdevice_id[TAS5806MD] }, + { "TXNW5815", (kernel_ulong_t)&tasdevice_id[TAS5815] }, + { "TXNW5822", (kernel_ulong_t)&tasdevice_id[TAS5822] }, + { "TXNW5825", (kernel_ulong_t)&tasdevice_id[TAS5825] }, + { "TXNW5827", (kernel_ulong_t)&tasdevice_id[TAS5827] }, + { "TXNW5828", (kernel_ulong_t)&tasdevice_id[TAS5828] }, + { "TXNW5830", (kernel_ulong_t)&tasdevice_id[TAS5830] }, {}, }; @@ -2121,7 +2120,6 @@ static struct i2c_driver tasdevice_i2c_driver = { }, .probe = tasdevice_i2c_probe, .remove = tasdevice_i2c_remove, - .id_table = tasdevice_id, }; module_i2c_driver(tasdevice_i2c_driver); From f4ee8a882a560308c390cf45ffa616c59f60a731 Mon Sep 17 00:00:00 2001 From: Chaitanya Sabnis Date: Fri, 27 Mar 2026 14:51:06 +0530 Subject: [PATCH 593/700] ASoC: dt-bindings: hisilicon: Convert hi6210 I2S to dt-schema Convert the Hisilicon hi6210 I2S controller hardware binding from legacy plain text to modern YAML dt-schema format. During the conversion, the order of the dma-names properties in the example was corrected to "tx", "rx" to match the official property description, resolving a contradiction in the original text binding. Signed-off-by: Chaitanya Sabnis Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260327092106.4233-1-chaitanya.msabnis@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/hisilicon,hi6210-i2s.txt | 42 ---------- .../bindings/sound/hisilicon,hi6210-i2s.yaml | 80 +++++++++++++++++++ 2 files changed, 80 insertions(+), 42 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt create mode 100644 Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt deleted file mode 100644 index 7a296784eb3748..00000000000000 --- a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt +++ /dev/null @@ -1,42 +0,0 @@ -* Hisilicon 6210 i2s controller - -Required properties: - -- compatible: should be one of the following: - - "hisilicon,hi6210-i2s" -- reg: physical base address of the i2s controller unit and length of - memory mapped region. -- interrupts: should contain the i2s interrupt. -- clocks: a list of phandle + clock-specifier pairs, one for each entry - in clock-names. -- clock-names: should contain following: - - "dacodec" - - "i2s-base" -- dmas: DMA specifiers for tx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt -- dma-names: should be "tx" and "rx" -- hisilicon,sysctrl-syscon: phandle to sysctrl syscon -- #sound-dai-cells: Should be set to 1 (for multi-dai) - - The dai cell indexes reference the following interfaces: - 0: S2 interface - (Currently that is the only one available, but more may be - supported in the future) - -Example for the hi6210 i2s controller: - -i2s0: i2s@f7118000{ - compatible = "hisilicon,hi6210-i2s"; - reg = <0x0 0xf7118000 0x0 0x8000>; /* i2s unit */ - interrupts = ; /* 155 "DigACodec_intr"-32 */ - clocks = <&sys_ctrl HI6220_DACODEC_PCLK>, - <&sys_ctrl HI6220_BBPPLL0_DIV>; - clock-names = "dacodec", "i2s-base"; - dmas = <&dma0 15 &dma0 14>; - dma-names = "rx", "tx"; - hisilicon,sysctrl-syscon = <&sys_ctrl>; - #sound-dai-cells = <1>; -}; - -Then when referencing the i2s controller: - sound-dai = <&i2s0 0>; /* index 0 => S2 interface */ - diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml new file mode 100644 index 00000000000000..5171f984630bec --- /dev/null +++ b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/hisilicon,hi6210-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HiSilicon hi6210 I2S controller + +maintainers: + - John Stultz + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: hisilicon,hi6210-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: dacodec + - const: i2s-base + + dmas: + maxItems: 2 + + dma-names: + items: + - const: tx + - const: rx + + hisilicon,sysctrl-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to sysctrl syscon + + "#sound-dai-cells": + const: 1 + description: | + The dai cell indexes reference the following interfaces: + 0: S2 interface + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - hisilicon,sysctrl-syscon + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2s@f7118000 { + compatible = "hisilicon,hi6210-i2s"; + reg = <0xf7118000 0x8000>; + interrupts = ; + clocks = <&sys_ctrl HI6220_DACODEC_PCLK>, + <&sys_ctrl HI6220_BBPPLL0_DIV>; + clock-names = "dacodec", "i2s-base"; + dmas = <&dma0 14>, <&dma0 15>; + dma-names = "tx", "rx"; + hisilicon,sysctrl-syscon = <&sys_ctrl>; + #sound-dai-cells = <1>; + }; From 52f657e34d7b21b47434d9d8b26fa7f6778b63a0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Apr 2026 13:18:57 -0700 Subject: [PATCH 594/700] x86: shadow stacks: proper error handling for mmap lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 김영민 reports that shstk_pop_sigframe() doesn't check for errors from mmap_read_lock_killable(), which is a silly oversight, and also shows that we haven't marked those functions with "__must_check", which would have immediately caught it. So let's fix both issues. Reported-by: 김영민 Acked-by: Oleg Nesterov Acked-by: Dave Hansen Acked-by: Rick Edgecombe Signed-off-by: Linus Torvalds --- arch/x86/kernel/shstk.c | 3 ++- include/linux/mmap_lock.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index 978232b6d48d76..ff8edea8511b48 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -351,7 +351,8 @@ static int shstk_pop_sigframe(unsigned long *ssp) need_to_check_vma = PAGE_ALIGN(*ssp) == *ssp; if (need_to_check_vma) - mmap_read_lock_killable(current->mm); + if (mmap_read_lock_killable(current->mm)) + return -EINTR; err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp); if (unlikely(err)) diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h index 93eca48bc443ab..04b8f61ece5d8e 100644 --- a/include/linux/mmap_lock.h +++ b/include/linux/mmap_lock.h @@ -546,7 +546,7 @@ static inline void mmap_write_lock_nested(struct mm_struct *mm, int subclass) __mmap_lock_trace_acquire_returned(mm, true, true); } -static inline int mmap_write_lock_killable(struct mm_struct *mm) +static inline int __must_check mmap_write_lock_killable(struct mm_struct *mm) { int ret; @@ -593,7 +593,7 @@ static inline void mmap_read_lock(struct mm_struct *mm) __mmap_lock_trace_acquire_returned(mm, false, true); } -static inline int mmap_read_lock_killable(struct mm_struct *mm) +static inline int __must_check mmap_read_lock_killable(struct mm_struct *mm) { int ret; @@ -603,7 +603,7 @@ static inline int mmap_read_lock_killable(struct mm_struct *mm) return ret; } -static inline bool mmap_read_trylock(struct mm_struct *mm) +static inline bool __must_check mmap_read_trylock(struct mm_struct *mm) { bool ret; From c09ea768bdb975e828f8e17293c397c3d14ad85d Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 5 Apr 2026 14:51:52 +0800 Subject: [PATCH 595/700] net: mdio: realtek-rtl9300: use scoped device_for_each_child_node loop Switch to device_for_each_child_node_scoped() to auto-release fwnode references on early exit. Fixes: 24e31e474769 ("net: mdio: Add RTL9300 MDIO driver") Signed-off-by: Felix Gu Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260405-rtl9300-v1-1-08e4499cf944@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/mdio/mdio-realtek-rtl9300.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index 405a07075dd115..8d5fb014ca06c4 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -466,7 +466,6 @@ static int rtl9300_mdiobus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rtl9300_mdio_priv *priv; - struct fwnode_handle *child; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -487,7 +486,7 @@ static int rtl9300_mdiobus_probe(struct platform_device *pdev) if (err) return err; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { err = rtl9300_mdiobus_probe_one(dev, priv, child); if (err) return err; From bdbfead6d38979475df0c2f4bad2b19394fe9bdc Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:29 +0100 Subject: [PATCH 596/700] rxrpc: Fix key quota calculation for multitoken keys In the rxrpc key preparsing, every token extracted sets the proposed quota value, but for multitoken keys, this will overwrite the previous proposed quota, losing it. Fix this by adding to the proposed quota instead. Fixes: 8a7a3eb4ddbe ("KEYS: RxRPC: Use key preparsing") Closes: https://sashiko.dev/#/patchset/20260319150150.4189381-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/key.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 85078114b2dd70..af403f0ccab536 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -72,7 +72,7 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, return -EKEYREJECTED; plen = sizeof(*token) + sizeof(*token->kad) + tktlen; - prep->quotalen = datalen + plen; + prep->quotalen += datalen + plen; plen -= sizeof(*token); token = kzalloc_obj(*token); @@ -199,7 +199,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, } plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen; - prep->quotalen = datalen + plen; + prep->quotalen += datalen + plen; plen -= sizeof(*token); token = kzalloc_obj(*token); @@ -460,6 +460,7 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) memcpy(&kver, prep->data, sizeof(kver)); prep->data += sizeof(kver); prep->datalen -= sizeof(kver); + prep->quotalen = 0; _debug("KEY I/F VERSION: %u", kver); @@ -497,7 +498,7 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) goto error; plen = sizeof(*token->kad) + v1->ticket_length; - prep->quotalen = plen + sizeof(*token); + prep->quotalen += plen + sizeof(*token); ret = -ENOMEM; token = kzalloc_obj(*token); From b555912b9b21075e8298015f888ffe3ff60b1a97 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:30 +0100 Subject: [PATCH 597/700] rxrpc: Fix key parsing memleak In rxrpc_preparse_xdr_yfs_rxgk(), the memory attached to token->rxgk can be leaked in a few error paths after it's allocated. Fix this by freeing it in the "reject_token:" case. Fixes: 0ca100ff4df6 ("rxrpc: Add YFS RxGK (GSSAPI) security class") Closes: https://sashiko.dev/#/patchset/20260319150150.4189381-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index af403f0ccab536..26d4336a4a023f 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -274,6 +274,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, nomem: return -ENOMEM; reject_token: + kfree(token->rxgk); kfree(token); reject: return -EKEYREJECTED; From 6a59d84b4fc2f27f7b40e348506cc686712e260b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:31 +0100 Subject: [PATCH 598/700] rxrpc: Fix anonymous key handling In rxrpc_new_client_call_for_sendmsg(), a key with no payload is meant to be substituted for a NULL key pointer, but the variable this is done with is subsequently not used. Fix this by using "key" rather than "rx->key" when filling in the connection parameters. Note that this only affects direct use of AF_RXRPC; the kAFS filesystem doesn't use sendmsg() directly and so bypasses the issue. Further, AF_RXRPC passes a NULL key in if no key is set, so using an anonymous key in that manner works. Since this hasn't been noticed to this point, it might be better just to remove the "key" variable and the code that sets it - and, arguably, rxrpc_init_client_call_security() would be a better place to handle it. Fixes: 19ffa01c9c45 ("rxrpc: Use structs to hold connection params and protocol info") Closes: https://sashiko.dev/#/patchset/20260319150150.4189381-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/sendmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 04f9c5f2dc2451..c35de4fd75e311 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -637,7 +637,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, memset(&cp, 0, sizeof(cp)); cp.local = rx->local; cp.peer = peer; - cp.key = rx->key; + cp.key = key; cp.security_level = rx->min_sec_level; cp.exclusive = rx->exclusive | p->exclusive; cp.upgrade = p->upgrade; From 146d4ab94cf129ee06cd467cb5c71368a6b5bad6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:32 +0100 Subject: [PATCH 599/700] rxrpc: Fix call removal to use RCU safe deletion Fix rxrpc call removal from the rxnet->calls list to use list_del_rcu() rather than list_del_init() to prevent stuffing up reading /proc/net/rxrpc/calls from potentially getting into an infinite loop. This, however, means that list_empty() no longer works on an entry that's been deleted from the list, making it harder to detect prior deletion. Fix this by: Firstly, make rxrpc_destroy_all_calls() only dump the first ten calls that are unexpectedly still on the list. Limiting the number of steps means there's no need to call cond_resched() or to remove calls from the list here, thereby eliminating the need for rxrpc_put_call() to check for that. rxrpc_put_call() can then be fixed to unconditionally delete the call from the list as it is the only place that the deletion occurs. Fixes: 2baec2c3f854 ("rxrpc: Support network namespacing") Closes: https://sashiko.dev/#/patchset/20260319150150.4189381-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Linus Torvalds cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-5-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- include/trace/events/rxrpc.h | 2 +- net/rxrpc/call_object.c | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 869f97c9bf733e..a826cd80007b34 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -347,7 +347,7 @@ EM(rxrpc_call_see_release, "SEE release ") \ EM(rxrpc_call_see_userid_exists, "SEE u-exists") \ EM(rxrpc_call_see_waiting_call, "SEE q-conn ") \ - E_(rxrpc_call_see_zap, "SEE zap ") + E_(rxrpc_call_see_still_live, "SEE !still-l") #define rxrpc_txqueue_traces \ EM(rxrpc_txqueue_await_reply, "AWR") \ diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 918f41d97a2f93..59329cfe15322c 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -654,11 +654,9 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace why) if (dead) { ASSERTCMP(__rxrpc_call_state(call), ==, RXRPC_CALL_COMPLETE); - if (!list_empty(&call->link)) { - spin_lock(&rxnet->call_lock); - list_del_init(&call->link); - spin_unlock(&rxnet->call_lock); - } + spin_lock(&rxnet->call_lock); + list_del_rcu(&call->link); + spin_unlock(&rxnet->call_lock); rxrpc_cleanup_call(call); } @@ -730,24 +728,20 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet) _enter(""); if (!list_empty(&rxnet->calls)) { - spin_lock(&rxnet->call_lock); + int shown = 0; - while (!list_empty(&rxnet->calls)) { - call = list_entry(rxnet->calls.next, - struct rxrpc_call, link); - _debug("Zapping call %p", call); + spin_lock(&rxnet->call_lock); - rxrpc_see_call(call, rxrpc_call_see_zap); - list_del_init(&call->link); + list_for_each_entry(call, &rxnet->calls, link) { + rxrpc_see_call(call, rxrpc_call_see_still_live); pr_err("Call %p still in use (%d,%s,%lx,%lx)!\n", call, refcount_read(&call->ref), rxrpc_call_states[__rxrpc_call_state(call)], call->flags, call->events); - spin_unlock(&rxnet->call_lock); - cond_resched(); - spin_lock(&rxnet->call_lock); + if (++shown >= 10) + break; } spin_unlock(&rxnet->call_lock); From d179a868dd755b0cfcf7582e00943d702b9943b8 Mon Sep 17 00:00:00 2001 From: Oleh Konko Date: Wed, 8 Apr 2026 13:12:33 +0100 Subject: [PATCH 600/700] rxrpc: Fix RxGK token loading to check bounds rxrpc_preparse_xdr_yfs_rxgk() reads the raw key length and ticket length from the XDR token as u32 values and passes each through round_up(x, 4) before using the rounded value for validation and allocation. When the raw length is >= 0xfffffffd, round_up() wraps to 0, so the bounds check and kzalloc both use 0 while the subsequent memcpy still copies the original ~4 GiB value, producing a heap buffer overflow reachable from an unprivileged add_key() call. Fix this by: (1) Rejecting raw key lengths above AFSTOKEN_GK_KEY_MAX and raw ticket lengths above AFSTOKEN_GK_TOKEN_MAX before rounding, consistent with the caps that the RxKAD path already enforces via AFSTOKEN_RK_TIX_MAX. (2) Sizing the flexible-array allocation from the validated raw key length via struct_size_t() instead of the rounded value. (3) Caching the raw lengths so that the later field assignments and memcpy calls do not re-read from the token, eliminating a class of TOCTOU re-parse. The control path (valid token with lengths within bounds) is unaffected. Fixes: 0ca100ff4df6 ("rxrpc: Add YFS RxGK (GSSAPI) security class") Signed-off-by: Oleh Konko Signed-off-by: David Howells Reviewed-by: Jeffrey Altman cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-6-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/key.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 26d4336a4a023f..77237a82be3be0 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -171,7 +172,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, size_t plen; const __be32 *ticket, *key; s64 tmp; - u32 tktlen, keylen; + size_t raw_keylen, raw_tktlen, keylen, tktlen; _enter(",{%x,%x,%x,%x},%x", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), @@ -181,18 +182,22 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, goto reject; key = xdr + (6 * 2 + 1); - keylen = ntohl(key[-1]); - _debug("keylen: %x", keylen); - keylen = round_up(keylen, 4); + raw_keylen = ntohl(key[-1]); + _debug("keylen: %zx", raw_keylen); + if (raw_keylen > AFSTOKEN_GK_KEY_MAX) + goto reject; + keylen = round_up(raw_keylen, 4); if ((6 * 2 + 2) * 4 + keylen > toklen) goto reject; ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1); - tktlen = ntohl(ticket[-1]); - _debug("tktlen: %x", tktlen); - tktlen = round_up(tktlen, 4); + raw_tktlen = ntohl(ticket[-1]); + _debug("tktlen: %zx", raw_tktlen); + if (raw_tktlen > AFSTOKEN_GK_TOKEN_MAX) + goto reject; + tktlen = round_up(raw_tktlen, 4); if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) { - kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]", + kleave(" = -EKEYREJECTED [%zx!=%x, %zx,%zx]", (6 * 2 + 2) * 4 + keylen + tktlen, toklen, keylen, tktlen); goto reject; @@ -206,7 +211,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, if (!token) goto nomem; - token->rxgk = kzalloc(sizeof(*token->rxgk) + keylen, GFP_KERNEL); + token->rxgk = kzalloc(struct_size_t(struct rxgk_key, _key, raw_keylen), GFP_KERNEL); if (!token->rxgk) goto nomem_token; @@ -221,9 +226,9 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, token->rxgk->enctype = tmp = xdr_dec64(xdr + 5 * 2); if (tmp < 0 || tmp > UINT_MAX) goto reject_token; - token->rxgk->key.len = ntohl(key[-1]); + token->rxgk->key.len = raw_keylen; token->rxgk->key.data = token->rxgk->_key; - token->rxgk->ticket.len = ntohl(ticket[-1]); + token->rxgk->ticket.len = raw_tktlen; if (token->rxgk->endtime != 0) { expiry = rxrpc_s64_to_time64(token->rxgk->endtime); @@ -236,8 +241,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, memcpy(token->rxgk->key.data, key, token->rxgk->key.len); /* Pad the ticket so that we can use it directly in XDR */ - token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4), - GFP_KERNEL); + token->rxgk->ticket.data = kzalloc(tktlen, GFP_KERNEL); if (!token->rxgk->ticket.data) goto nomem_yrxgk; memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len); From b33f5741bb187db8ff32e8f5b96def77cc94dfca Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Wed, 8 Apr 2026 13:12:34 +0100 Subject: [PATCH 601/700] rxrpc: Fix use of wrong skb when comparing queued RESP challenge serial In rxrpc_post_response(), the code should be comparing the challenge serial number from the cached response before deciding to switch to a newer response, but looks at the newer packet private data instead, rendering the comparison always false. Fix this by switching to look at the older packet. Fix further[1] to substitute the new packet in place of the old one if newer and also to release whichever we don't use. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Signed-off-by: Alok Tiwari Signed-off-by: David Howells Reviewed-by: Jeffrey Altman cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://sashiko.dev/#/patchset/20260319150150.4189381-1-dhowells%40redhat.com [1] Link: https://patch.msgid.link/20260408121252.2249051-7-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- include/trace/events/rxrpc.h | 1 + net/rxrpc/conn_event.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index a826cd80007b34..f7f559204b8733 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -185,6 +185,7 @@ EM(rxrpc_skb_put_input, "PUT input ") \ EM(rxrpc_skb_put_jumbo_subpacket, "PUT jumbo-sub") \ EM(rxrpc_skb_put_oob, "PUT oob ") \ + EM(rxrpc_skb_put_old_response, "PUT old-resp ") \ EM(rxrpc_skb_put_purge, "PUT purge ") \ EM(rxrpc_skb_put_purge_oob, "PUT purge-oob") \ EM(rxrpc_skb_put_response, "PUT response ") \ diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 98ad9b51ca2cdc..c50cbfc5a313c2 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -557,11 +557,11 @@ void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb) spin_lock_irq(&local->lock); old = conn->tx_response; if (old) { - struct rxrpc_skb_priv *osp = rxrpc_skb(skb); + struct rxrpc_skb_priv *osp = rxrpc_skb(old); /* Always go with the response to the most recent challenge. */ if (after(sp->resp.challenge_serial, osp->resp.challenge_serial)) - conn->tx_response = old; + conn->tx_response = skb; else old = skb; } else { @@ -569,4 +569,5 @@ void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb) } spin_unlock_irq(&local->lock); rxrpc_poke_conn(conn, rxrpc_conn_get_poke_response); + rxrpc_free_skb(old, rxrpc_skb_put_old_response); } From 65b3ffe0972ed023acc3981a0f7e1ae5d0208bd3 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Wed, 8 Apr 2026 13:12:35 +0100 Subject: [PATCH 602/700] rxrpc: Fix rack timer warning to report unexpected mode rxrpc_rack_timer_expired() clears call->rack_timer_mode to OFF before the switch. The default case warning therefore always prints OFF and doesn't identify the unexpected timer mode. Log the saved mode value instead so the warning reports the actual unexpected rack timer mode. Fixes: 7c482665931b ("rxrpc: Implement RACK/TLP to deal with transmission stalls [RFC8985]") Signed-off-by: Alok Tiwari Signed-off-by: David Howells Reviewed-by: Simon Horman Reviewed-by: Jeffrey Altman cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-8-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/input_rack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/input_rack.c b/net/rxrpc/input_rack.c index 13c371261e0a58..9eb109ffba56e8 100644 --- a/net/rxrpc/input_rack.c +++ b/net/rxrpc/input_rack.c @@ -413,6 +413,6 @@ void rxrpc_rack_timer_expired(struct rxrpc_call *call, ktime_t overran_by) break; //case RXRPC_CALL_RACKTIMER_ZEROWIN: default: - pr_warn("Unexpected rack timer %u", call->rack_timer_mode); + pr_warn("Unexpected rack timer %u", mode); } } From d666540d217e8d420544ebdfbadeedd623562733 Mon Sep 17 00:00:00 2001 From: Anderson Nascimento Date: Wed, 8 Apr 2026 13:12:36 +0100 Subject: [PATCH 603/700] rxrpc: Fix key reference count leak from call->key When creating a client call in rxrpc_alloc_client_call(), the code obtains a reference to the key. This is never cleaned up and gets leaked when the call is destroyed. Fix this by freeing call->key in rxrpc_destroy_call(). Before the patch, it shows the key reference counter elevated: $ cat /proc/keys | grep afs@54321 1bffe9cd I--Q--i 8053480 4169w 3b010000 1000 1000 rxrpc afs@54321: ka $ After the patch, the invalidated key is removed when the code exits: $ cat /proc/keys | grep afs@54321 $ Fixes: f3441d4125fc ("rxrpc: Copy client call parameters into rxrpc_call earlier") Signed-off-by: Anderson Nascimento Co-developed-by: David Howells Signed-off-by: David Howells Reviewed-by: Jeffrey Altman cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-9-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/call_object.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 59329cfe15322c..f035f486c13973 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -692,6 +692,7 @@ static void rxrpc_destroy_call(struct work_struct *work) rxrpc_put_bundle(call->bundle, rxrpc_bundle_put_call); rxrpc_put_peer(call->peer, rxrpc_peer_put_call); rxrpc_put_local(call->local, rxrpc_local_put_call); + key_put(call->key); call_rcu(&call->rcu, rxrpc_rcu_free_call); } From 0cd3e3f3f2ec1a45aa559e2c0f3d57fac5eb3c25 Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Wed, 8 Apr 2026 13:12:37 +0100 Subject: [PATCH 604/700] rxrpc: Fix to request an ack if window is limited Peers may only send immediate acks for every 2 UDP packets received. When sending a jumbogram, it is important to check that there is sufficient window space to send another same sized jumbogram following the current one, and request an ack if there isn't. Failure to do so may cause the call to stall waiting for an ack until the resend timer fires. Where jumbograms are in use this causes a very significant drop in performance. Fixes: fe24a5494390 ("rxrpc: Send jumbo DATA packets") Signed-off-by: Marc Dionne Signed-off-by: David Howells cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-10-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- include/trace/events/rxrpc.h | 1 + net/rxrpc/ar-internal.h | 2 +- net/rxrpc/output.c | 2 ++ net/rxrpc/proc.c | 5 +++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index f7f559204b8733..578b8038b21178 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -521,6 +521,7 @@ #define rxrpc_req_ack_traces \ EM(rxrpc_reqack_ack_lost, "ACK-LOST ") \ EM(rxrpc_reqack_app_stall, "APP-STALL ") \ + EM(rxrpc_reqack_jumbo_win, "JUMBO-WIN ") \ EM(rxrpc_reqack_more_rtt, "MORE-RTT ") \ EM(rxrpc_reqack_no_srv_last, "NO-SRVLAST") \ EM(rxrpc_reqack_old_rtt, "OLD-RTT ") \ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 36d6ca0d1089e1..96ecb83c907153 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -117,7 +117,7 @@ struct rxrpc_net { atomic_t stat_tx_jumbo[10]; atomic_t stat_rx_jumbo[10]; - atomic_t stat_why_req_ack[8]; + atomic_t stat_why_req_ack[9]; atomic_t stat_io_loop; }; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index d70db367e358db..870e59bf06af2b 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -479,6 +479,8 @@ static size_t rxrpc_prepare_data_subpacket(struct rxrpc_call *call, why = rxrpc_reqack_old_rtt; else if (!last && !after(READ_ONCE(call->send_top), txb->seq)) why = rxrpc_reqack_app_stall; + else if (call->tx_winsize <= (2 * req->n) || call->cong_cwnd <= (2 * req->n)) + why = rxrpc_reqack_jumbo_win; else goto dont_set_request_ack; diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 59292f7f9205e7..7755fca5beb86a 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -518,11 +518,12 @@ int rxrpc_stats_show(struct seq_file *seq, void *v) atomic_read(&rxnet->stat_rx_acks[RXRPC_ACK_IDLE]), atomic_read(&rxnet->stat_rx_acks[0])); seq_printf(seq, - "Why-Req-A: acklost=%u mrtt=%u ortt=%u stall=%u\n", + "Why-Req-A: acklost=%u mrtt=%u ortt=%u stall=%u jwin=%u\n", atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_ack_lost]), atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_more_rtt]), atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_old_rtt]), - atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_app_stall])); + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_app_stall]), + atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_jumbo_win])); seq_printf(seq, "Why-Req-A: nolast=%u retx=%u slows=%u smtxw=%u\n", atomic_read(&rxnet->stat_why_req_ack[rxrpc_reqack_no_srv_last]), From 6331f1b24a3e85465f6454e003a3e6c22005a5c5 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Wed, 8 Apr 2026 13:12:38 +0100 Subject: [PATCH 605/700] rxrpc: Only put the call ref if one was acquired rxrpc_input_packet_on_conn() can process a to-client packet after the current client call on the channel has already been torn down. In that case chan->call is NULL, rxrpc_try_get_call() returns NULL and there is no reference to drop. The client-side implicit-end error path does not account for that and unconditionally calls rxrpc_put_call(). This turns a protocol error path into a kernel crash instead of rejecting the packet. Only drop the call reference if one was actually acquired. Keep the existing protocol error handling unchanged. Fixes: 5e6ef4f1017c ("rxrpc: Make the I/O thread take over the call and local processor work") Reported-by: Yifan Wu Reported-by: Juefei Pu Signed-off-by: Douya Le Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Ao Zhou Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-11-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/io_thread.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index e939ecf417c4b5..69795693192521 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -419,7 +419,8 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn, if (sp->hdr.callNumber > chan->call_id) { if (rxrpc_to_client(sp)) { - rxrpc_put_call(call, rxrpc_call_put_input); + if (call) + rxrpc_put_call(call, rxrpc_call_put_input); return rxrpc_protocol_error(skb, rxrpc_eproto_unexpected_implicit_end); } From fe4447cd95623b1cfacc15f280aab73a6d7340b2 Mon Sep 17 00:00:00 2001 From: Yuqi Xu Date: Wed, 8 Apr 2026 13:12:39 +0100 Subject: [PATCH 606/700] rxrpc: reject undecryptable rxkad response tickets rxkad_decrypt_ticket() decrypts the RXKAD response ticket and then parses the buffer as plaintext without checking whether crypto_skcipher_decrypt() succeeded. A malformed RESPONSE can therefore use a non-block-aligned ticket length, make the decrypt operation fail, and still drive the ticket parser with attacker-controlled bytes. Check the decrypt result and abort the connection with RXKADBADTICKET when ticket decryption fails. Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-12-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxkad.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index e923d6829008a6..0f79d694cb0889 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -958,6 +958,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, struct in_addr addr; unsigned int life; time64_t issue, now; + int ret; bool little_endian; u8 *p, *q, *name, *end; @@ -977,8 +978,11 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, sg_init_one(&sg[0], ticket, ticket_len); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, ticket_len, iv.x); - crypto_skcipher_decrypt(req); + ret = crypto_skcipher_decrypt(req); skcipher_request_free(req); + if (ret < 0) + return rxrpc_abort_conn(conn, skb, RXKADBADTICKET, -EPROTO, + rxkad_abort_resp_tkt_short); p = ticket; end = p + ticket_len; From 3e3138007887504ee9206d0bfb5acb062c600025 Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Wed, 8 Apr 2026 13:12:40 +0100 Subject: [PATCH 607/700] rxrpc: fix RESPONSE authenticator parser OOB read rxgk_verify_authenticator() copies auth_len bytes into a temporary buffer and then passes p + auth_len as the parser limit to rxgk_do_verify_authenticator(). Since p is a __be32 *, that inflates the parser end pointer by a factor of four and lets malformed RESPONSE authenticators read past the kmalloc() buffer. Decoded from the original latest-net reproduction logs with scripts/decode_stacktrace.sh: BUG: KASAN: slab-out-of-bounds in rxgk_verify_response() Call Trace: dump_stack_lvl() [lib/dump_stack.c:123] print_report() [mm/kasan/report.c:379 mm/kasan/report.c:482] kasan_report() [mm/kasan/report.c:597] rxgk_verify_response() [net/rxrpc/rxgk.c:1103 net/rxrpc/rxgk.c:1167 net/rxrpc/rxgk.c:1274] rxrpc_process_connection() [net/rxrpc/conn_event.c:266 net/rxrpc/conn_event.c:364 net/rxrpc/conn_event.c:386] process_one_work() [kernel/workqueue.c:3281] worker_thread() [kernel/workqueue.c:3353 kernel/workqueue.c:3440] kthread() [kernel/kthread.c:436] ret_from_fork() [arch/x86/kernel/process.c:164] Allocated by task 54: rxgk_verify_response() [include/linux/slab.h:954 net/rxrpc/rxgk.c:1155 net/rxrpc/rxgk.c:1274] rxrpc_process_connection() [net/rxrpc/conn_event.c:266 net/rxrpc/conn_event.c:364 net/rxrpc/conn_event.c:386] Convert the byte count to __be32 units before constructing the parser limit. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Signed-off-by: Keenan Dong Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: Willy Tarreau cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-13-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxgk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index f9f5a2dc62ed0c..01dbdf0b5cf2e4 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1164,7 +1164,8 @@ static int rxgk_verify_authenticator(struct rxrpc_connection *conn, } p = auth; - ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, p + auth_len); + ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, + p + auth_len / sizeof(*p)); error: kfree(auth); return ret; From a2567217ade970ecc458144b6be469bc015b23e5 Mon Sep 17 00:00:00 2001 From: Keenan Dong Date: Wed, 8 Apr 2026 13:12:41 +0100 Subject: [PATCH 608/700] rxrpc: fix oversized RESPONSE authenticator length check rxgk_verify_response() decodes auth_len from the packet and is supposed to verify that it fits in the remaining bytes. The existing check is inverted, so oversized RESPONSE authenticators are accepted and passed to rxgk_decrypt_skb(), which can later reach skb_to_sgvec() with an impossible length and hit BUG_ON(len). Decoded from the original latest-net reproduction logs with scripts/decode_stacktrace.sh: RIP: __skb_to_sgvec() [net/core/skbuff.c:5285 (discriminator 1)] Call Trace: skb_to_sgvec() [net/core/skbuff.c:5305] rxgk_decrypt_skb() [net/rxrpc/rxgk_common.h:81] rxgk_verify_response() [net/rxrpc/rxgk.c:1268] rxrpc_process_connection() [net/rxrpc/conn_event.c:266 net/rxrpc/conn_event.c:364 net/rxrpc/conn_event.c:386] process_one_work() [kernel/workqueue.c:3281] worker_thread() [kernel/workqueue.c:3353 kernel/workqueue.c:3440] kthread() [kernel/kthread.c:436] ret_from_fork() [arch/x86/kernel/process.c:164] Reject authenticator lengths that exceed the remaining packet payload. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Signed-off-by: Keenan Dong Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: Willy Tarreau cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-14-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxgk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 01dbdf0b5cf2e4..9e4a4ff28913c4 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1224,7 +1224,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, auth_offset = offset; auth_len = ntohl(xauth_len); - if (auth_len < len) + if (auth_len > len) goto short_packet; if (auth_len & 3) goto inconsistent; From f125846ee79fcae537a964ce66494e96fa54a6de Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Wed, 8 Apr 2026 13:12:42 +0100 Subject: [PATCH 609/700] rxrpc: fix reference count leak in rxrpc_server_keyring() This patch fixes a reference count leak in rxrpc_server_keyring() by checking if rx->securities is already set. Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-15-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/server_key.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rxrpc/server_key.c b/net/rxrpc/server_key.c index 36b05fd842a7b1..27491f1e127366 100644 --- a/net/rxrpc/server_key.c +++ b/net/rxrpc/server_key.c @@ -125,6 +125,9 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, sockptr_t optval, int optlen) _enter(""); + if (rx->securities) + return -EINVAL; + if (optlen <= 0 || optlen > PAGE_SIZE - 1) return -EINVAL; From 2afd86ccbb2082a3c4258aea8c07e5bb6267bc2f Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:43 +0100 Subject: [PATCH 610/700] rxrpc: Fix key/keyring checks in setsockopt(RXRPC_SECURITY_KEY/KEYRING) An AF_RXRPC socket can be both client and server at the same time. When sending new calls (ie. it's acting as a client), it uses rx->key to set the security, and when accepting incoming calls (ie. it's acting as a server), it uses rx->securities. setsockopt(RXRPC_SECURITY_KEY) sets rx->key to point to an rxrpc-type key and setsockopt(RXRPC_SECURITY_KEYRING) sets rx->securities to point to a keyring of rxrpc_s-type keys. Now, it should be possible to use both rx->key and rx->securities on the same socket - but for userspace AF_RXRPC sockets rxrpc_setsockopt() prevents that. Fix this by: (1) Remove the incorrect check rxrpc_setsockopt(RXRPC_SECURITY_KEYRING) makes on rx->key. (2) Move the check that rxrpc_setsockopt(RXRPC_SECURITY_KEY) makes on rx->key down into rxrpc_request_key(). (3) Remove rxrpc_request_key()'s check on rx->securities. This (in combination with a previous patch) pushes the checks down into the functions that set those pointers and removes the cross-checks that prevent both key and keyring being set. Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Closes: https://sashiko.dev/#/patchset/20260401105614.1696001-10-dhowells@redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Anderson Nascimento cc: Luxiao Xu cc: Yuan Tan cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-16-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/af_rxrpc.c | 6 ------ net/rxrpc/key.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 0f90272ac254bb..32ec91fa938fbd 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -654,9 +654,6 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, goto success; case RXRPC_SECURITY_KEY: - ret = -EINVAL; - if (rx->key) - goto error; ret = -EISCONN; if (rx->sk.sk_state != RXRPC_UNBOUND) goto error; @@ -664,9 +661,6 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, goto error; case RXRPC_SECURITY_KEYRING: - ret = -EINVAL; - if (rx->key) - goto error; ret = -EISCONN; if (rx->sk.sk_state != RXRPC_UNBOUND) goto error; diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 77237a82be3be0..6301d79ee35a6c 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -622,7 +622,7 @@ int rxrpc_request_key(struct rxrpc_sock *rx, sockptr_t optval, int optlen) _enter(""); - if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities) + if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->key) return -EINVAL; description = memdup_sockptr_nul(optval, optlen); From f93af41b9f5f798823d0d0fb8765c2a936d76270 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:44 +0100 Subject: [PATCH 611/700] rxrpc: Fix missing error checks for rxkad encryption/decryption failure Add error checking for failure of crypto_skcipher_en/decrypt() to various rxkad function as the crypto functions can fail with ENOMEM at least. Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Closes: https://sashiko.dev/#/patchset/20260401105614.1696001-10-dhowells@redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-17-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxkad.c | 57 +++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 0f79d694cb0889..eb7f2769d2b121 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -197,6 +197,7 @@ static int rxkad_prime_packet_security(struct rxrpc_connection *conn, struct rxrpc_crypt iv; __be32 *tmpbuf; size_t tmpsize = 4 * sizeof(__be32); + int ret; _enter(""); @@ -225,13 +226,13 @@ static int rxkad_prime_packet_security(struct rxrpc_connection *conn, skcipher_request_set_sync_tfm(req, ci); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, &sg, &sg, tmpsize, iv.x); - crypto_skcipher_encrypt(req); + ret = crypto_skcipher_encrypt(req); skcipher_request_free(req); memcpy(&conn->rxkad.csum_iv, tmpbuf + 2, sizeof(conn->rxkad.csum_iv)); kfree(tmpbuf); - _leave(" = 0"); - return 0; + _leave(" = %d", ret); + return ret; } /* @@ -264,6 +265,7 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, struct scatterlist sg; size_t pad; u16 check; + int ret; _enter(""); @@ -286,11 +288,11 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, 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, 8, iv.x); - crypto_skcipher_encrypt(req); + ret = crypto_skcipher_encrypt(req); skcipher_request_zero(req); - _leave(" = 0"); - return 0; + _leave(" = %d", ret); + return ret; } /* @@ -345,7 +347,7 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) union { __be32 buf[2]; } crypto __aligned(8); - u32 x, y; + u32 x, y = 0; int ret; _enter("{%d{%x}},{#%u},%u,", @@ -376,8 +378,10 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) 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, 8, iv.x); - crypto_skcipher_encrypt(req); + ret = crypto_skcipher_encrypt(req); skcipher_request_zero(req); + if (ret < 0) + goto out; y = ntohl(crypto.buf[1]); y = (y >> 16) & 0xffff; @@ -413,6 +417,7 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) memset(p + txb->pkt_len, 0, gap); } +out: skcipher_request_free(req); _leave(" = %d [set %x]", ret, y); return ret; @@ -453,8 +458,10 @@ static int rxkad_verify_packet_1(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, 8, iv.x); - crypto_skcipher_decrypt(req); + ret = crypto_skcipher_decrypt(req); skcipher_request_zero(req); + if (ret < 0) + return ret; /* Extract the decrypted packet length */ if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) @@ -531,10 +538,14 @@ 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); - crypto_skcipher_decrypt(req); + ret = crypto_skcipher_decrypt(req); skcipher_request_zero(req); if (sg != _sg) kfree(sg); + if (ret < 0) { + WARN_ON_ONCE(ret != -ENOMEM); + return ret; + } /* Extract the decrypted packet length */ if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) @@ -602,8 +613,10 @@ static int rxkad_verify_packet(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, 8, iv.x); - crypto_skcipher_encrypt(req); + ret = crypto_skcipher_encrypt(req); skcipher_request_zero(req); + if (ret < 0) + goto out; y = ntohl(crypto.buf[1]); cksum = (y >> 16) & 0xffff; @@ -1077,21 +1090,23 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, /* * decrypt the response packet */ -static void rxkad_decrypt_response(struct rxrpc_connection *conn, - struct rxkad_response *resp, - const struct rxrpc_crypt *session_key) +static int rxkad_decrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxrpc_crypt *session_key) { struct skcipher_request *req = rxkad_ci_req; struct scatterlist sg[1]; struct rxrpc_crypt iv; + int ret; _enter(",,%08x%08x", ntohl(session_key->n[0]), ntohl(session_key->n[1])); mutex_lock(&rxkad_ci_mutex); - if (crypto_sync_skcipher_setkey(rxkad_ci, session_key->x, - sizeof(*session_key)) < 0) - BUG(); + ret = crypto_sync_skcipher_setkey(rxkad_ci, session_key->x, + sizeof(*session_key)); + if (ret < 0) + goto unlock; memcpy(&iv, session_key, sizeof(iv)); @@ -1100,12 +1115,14 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn, skcipher_request_set_sync_tfm(req, rxkad_ci); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); - crypto_skcipher_decrypt(req); + ret = crypto_skcipher_decrypt(req); skcipher_request_zero(req); +unlock: mutex_unlock(&rxkad_ci_mutex); _leave(""); + return ret; } /* @@ -1198,7 +1215,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, /* use the session key from inside the ticket to decrypt the * response */ - rxkad_decrypt_response(conn, response, &session_key); + ret = rxkad_decrypt_response(conn, response, &session_key); + if (ret < 0) + goto temporary_error_free_ticket; if (ntohl(response->encrypted.epoch) != conn->proto.epoch || ntohl(response->encrypted.cid) != conn->proto.cid || From 699e52180f4231c257821c037ed5c99d5eb0edb8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:45 +0100 Subject: [PATCH 612/700] rxrpc: Fix integer overflow in rxgk_verify_response() In rxgk_verify_response(), there's a potential integer overflow due to rounding up token_len before checking it, thereby allowing the length check to be bypassed. Fix this by checking the unrounded value against len too (len is limited as the response must fit in a single UDP packet). Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Closes: https://sashiko.dev/#/patchset/20260401105614.1696001-10-dhowells@redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-18-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxgk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 9e4a4ff28913c4..064c1531fc991d 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1209,7 +1209,8 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, token_offset = offset; token_len = ntohl(rhdr.token_len); - if (xdr_round_up(token_len) + sizeof(__be32) > len) + if (token_len > len || + xdr_round_up(token_len) + sizeof(__be32) > len) goto short_packet; trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, token_len); From 7e1876caa8363056f58a21d3b31b82c2daf7e608 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:46 +0100 Subject: [PATCH 613/700] rxrpc: Fix leak of rxgk context in rxgk_verify_response() Fix rxgk_verify_response() to clean up the rxgk context it creates. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Closes: https://sashiko.dev/#/patchset/20260401105614.1696001-10-dhowells@redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-19-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxgk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 064c1531fc991d..c67e3c2ca871a0 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1270,16 +1270,18 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, if (ret < 0) { rxrpc_abort_conn(conn, skb, RXGK_SEALEDINCON, ret, rxgk_abort_resp_auth_dec); - goto out; + goto out_gk; } ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len); if (ret < 0) - goto out; + goto out_gk; conn->key = key; key = NULL; ret = 0; +out_gk: + rxgk_put(gk); out: key_put(key); _leave(" = %d", ret); From f564af387c8c28238f8ebc13314c589d7ba8475d Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 8 Apr 2026 13:12:47 +0100 Subject: [PATCH 614/700] rxrpc: Fix buffer overread in rxgk_do_verify_authenticator() Fix rxgk_do_verify_authenticator() to check the buffer size before checking the nonce. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Closes: https://sashiko.dev/#/patchset/20260401105614.1696001-10-dhowells@redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-20-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/rxgk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index c67e3c2ca871a0..0d5e654da918f7 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1085,6 +1085,9 @@ static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn, _enter(""); + if ((end - p) * sizeof(__be32) < 24) + return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, + rxgk_abort_resp_short_auth); if (memcmp(p, conn->rxgk.nonce, 20) != 0) return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, rxgk_abort_resp_bad_nonce); @@ -1098,7 +1101,7 @@ static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn, p += xdr_round_up(app_len) / sizeof(__be32); if (end - p < 4) return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, - rxgk_abort_resp_short_applen); + rxgk_abort_resp_short_auth); level = ntohl(*p++); epoch = ntohl(*p++); From c43ffdcfdbb5567b1f143556df8a04b4eeea041c Mon Sep 17 00:00:00 2001 From: Wang Jie Date: Wed, 8 Apr 2026 13:12:48 +0100 Subject: [PATCH 615/700] rxrpc: only handle RESPONSE during service challenge Only process RESPONSE packets while the service connection is still in RXRPC_CONN_SERVICE_CHALLENGING. Check that state under state_lock before running response verification and security initialization, then use a local secured flag to decide whether to queue the secured-connection work after the state transition. This keeps duplicate or late RESPONSE packets from re-running the setup path and removes the unlocked post-transition state test. Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Signed-off-by: Jie Wang Signed-off-by: Yang Yang Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-21-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/conn_event.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index c50cbfc5a313c2..9a41ec708aeb9f 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -247,6 +247,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + bool secured = false; int ret; if (conn->state == RXRPC_CONN_ABORTED) @@ -262,6 +263,13 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return ret; case RXRPC_PACKET_TYPE_RESPONSE: + spin_lock_irq(&conn->state_lock); + if (conn->state != RXRPC_CONN_SERVICE_CHALLENGING) { + spin_unlock_irq(&conn->state_lock); + return 0; + } + spin_unlock_irq(&conn->state_lock); + ret = conn->security->verify_response(conn, skb); if (ret < 0) return ret; @@ -272,11 +280,13 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return ret; spin_lock_irq(&conn->state_lock); - if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) + if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) { conn->state = RXRPC_CONN_SERVICE; + secured = true; + } spin_unlock_irq(&conn->state_lock); - if (conn->state == RXRPC_CONN_SERVICE) { + if (secured) { /* Offload call state flipping to the I/O thread. As * we've already received the packet, put it on the * front of the queue. From a44ce6aa2efb61fe44f2cfab72bb01544bbca272 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 8 Apr 2026 13:12:49 +0100 Subject: [PATCH 616/700] rxrpc: proc: size address buffers for %pISpc output The AF_RXRPC procfs helpers format local and remote socket addresses into fixed 50-byte stack buffers with "%pISpc". That is too small for the longest current-tree IPv6-with-port form the formatter can produce. In lib/vsprintf.c, the compressed IPv6 path uses a dotted-quad tail not only for v4mapped addresses, but also for ISATAP addresses via ipv6_addr_is_isatap(). As a result, a case such as [ffff:ffff:ffff:ffff:0:5efe:255.255.255.255]:65535 is possible with the current formatter. That is 50 visible characters, so 51 bytes including the trailing NUL, which does not fit in the existing char[50] buffers used by net/rxrpc/proc.c. Size the buffers from the formatter's maximum textual form and switch the call sites to scnprintf(). Changes since v1: - correct the changelog to cite the actual maximum current-tree case explicitly - frame the proof around the ISATAP formatting path instead of the earlier mapped-v4 example Fixes: 75b54cb57ca3 ("rxrpc: Add IPv6 support") Signed-off-by: Pengpeng Hou Signed-off-by: David Howells cc: Marc Dionne cc: Anderson Nascimento cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260408121252.2249051-22-dhowells@redhat.com Signed-off-by: Jakub Kicinski --- net/rxrpc/proc.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index 7755fca5beb86a..e9a27fa7b25d8b 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -10,6 +10,10 @@ #include #include "ar-internal.h" +#define RXRPC_PROC_ADDRBUF_SIZE \ + (sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") + \ + sizeof(":12345")) + static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = { [RXRPC_CONN_UNUSED] = "Unused ", [RXRPC_CONN_CLIENT_UNSECURED] = "ClUnsec ", @@ -53,7 +57,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); enum rxrpc_call_state state; rxrpc_seq_t tx_bottom; - char lbuff[50], rbuff[50]; + char lbuff[RXRPC_PROC_ADDRBUF_SIZE], rbuff[RXRPC_PROC_ADDRBUF_SIZE]; long timeout = 0; if (v == &rxnet->calls) { @@ -69,11 +73,11 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v) local = call->local; if (local) - sprintf(lbuff, "%pISpc", &local->srx.transport); + scnprintf(lbuff, sizeof(lbuff), "%pISpc", &local->srx.transport); else strcpy(lbuff, "no_local"); - sprintf(rbuff, "%pISpc", &call->dest_srx.transport); + scnprintf(rbuff, sizeof(rbuff), "%pISpc", &call->dest_srx.transport); state = rxrpc_call_state(call); if (state != RXRPC_CALL_SERVER_PREALLOC) @@ -142,7 +146,7 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) struct rxrpc_connection *conn; struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); const char *state; - char lbuff[50], rbuff[50]; + char lbuff[RXRPC_PROC_ADDRBUF_SIZE], rbuff[RXRPC_PROC_ADDRBUF_SIZE]; if (v == &rxnet->conn_proc_list) { seq_puts(seq, @@ -161,8 +165,8 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) goto print; } - sprintf(lbuff, "%pISpc", &conn->local->srx.transport); - sprintf(rbuff, "%pISpc", &conn->peer->srx.transport); + scnprintf(lbuff, sizeof(lbuff), "%pISpc", &conn->local->srx.transport); + scnprintf(rbuff, sizeof(rbuff), "%pISpc", &conn->peer->srx.transport); print: state = rxrpc_is_conn_aborted(conn) ? rxrpc_call_completions[conn->completion] : @@ -228,7 +232,7 @@ static int rxrpc_bundle_seq_show(struct seq_file *seq, void *v) { struct rxrpc_bundle *bundle; struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); - char lbuff[50], rbuff[50]; + char lbuff[RXRPC_PROC_ADDRBUF_SIZE], rbuff[RXRPC_PROC_ADDRBUF_SIZE]; if (v == &rxnet->bundle_proc_list) { seq_puts(seq, @@ -242,8 +246,8 @@ static int rxrpc_bundle_seq_show(struct seq_file *seq, void *v) bundle = list_entry(v, struct rxrpc_bundle, proc_link); - sprintf(lbuff, "%pISpc", &bundle->local->srx.transport); - sprintf(rbuff, "%pISpc", &bundle->peer->srx.transport); + scnprintf(lbuff, sizeof(lbuff), "%pISpc", &bundle->local->srx.transport); + scnprintf(rbuff, sizeof(rbuff), "%pISpc", &bundle->peer->srx.transport); seq_printf(seq, "UDP %-47.47s %-47.47s %4x %3u %3d" " %c%c%c %08x | %08x %08x %08x %08x %08x\n", @@ -279,7 +283,7 @@ static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) { struct rxrpc_peer *peer; time64_t now; - char lbuff[50], rbuff[50]; + char lbuff[RXRPC_PROC_ADDRBUF_SIZE], rbuff[RXRPC_PROC_ADDRBUF_SIZE]; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -290,9 +294,9 @@ static int rxrpc_peer_seq_show(struct seq_file *seq, void *v) peer = list_entry(v, struct rxrpc_peer, hash_link); - sprintf(lbuff, "%pISpc", &peer->local->srx.transport); + scnprintf(lbuff, sizeof(lbuff), "%pISpc", &peer->local->srx.transport); - sprintf(rbuff, "%pISpc", &peer->srx.transport); + scnprintf(rbuff, sizeof(rbuff), "%pISpc", &peer->srx.transport); now = ktime_get_seconds(); seq_printf(seq, @@ -401,7 +405,7 @@ const struct seq_operations rxrpc_peer_seq_ops = { static int rxrpc_local_seq_show(struct seq_file *seq, void *v) { struct rxrpc_local *local; - char lbuff[50]; + char lbuff[RXRPC_PROC_ADDRBUF_SIZE]; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -412,7 +416,7 @@ static int rxrpc_local_seq_show(struct seq_file *seq, void *v) local = hlist_entry(v, struct rxrpc_local, link); - sprintf(lbuff, "%pISpc", &local->srx.transport); + scnprintf(lbuff, sizeof(lbuff), "%pISpc", &local->srx.transport); seq_printf(seq, "UDP %-47.47s %3u %3u %3u\n", From b30b1675aa2bcf0491fd3830b051df4e08a7c8ca Mon Sep 17 00:00:00 2001 From: Justin Iurman Date: Sat, 4 Apr 2026 15:41:37 +0200 Subject: [PATCH 617/700] net: ioam6: fix OOB and missing lock When trace->type.bit6 is set: if (trace->type.bit6) { ... queue = skb_get_tx_queue(dev, skb); qdisc = rcu_dereference(queue->qdisc); This code can lead to an out-of-bounds access of the dev->_tx[] array when is_input is true. In such a case, the packet is on the RX path and skb->queue_mapping contains the RX queue index of the ingress device. If the ingress device has more RX queues than the egress device (dev) has TX queues, skb_get_queue_mapping(skb) will exceed dev->num_tx_queues. Add a check to avoid this situation since skb_get_tx_queue() does not clamp the index. This issue has also revealed that per queue visibility cannot be accurate and will be replaced later as a new feature. While at it, add missing lock around qdisc_qstats_qlen_backlog(). The function __ioam6_fill_trace_data() is called from both softirq and process contexts, hence the use of spin_lock_bh() here. Fixes: b63c5478e9cb ("ipv6: ioam: Support for Queue depth data field") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20260403214418.2233266-2-kuba@kernel.org/ Signed-off-by: Justin Iurman Link: https://patch.msgid.link/20260404134137.24553-1-justin.iurman@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ioam6.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index 05a0b7d7e2aac3..e963a71858a78c 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -803,12 +803,16 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, struct Qdisc *qdisc; __u32 qlen, backlog; - if (dev->flags & IFF_LOOPBACK) { + if (dev->flags & IFF_LOOPBACK || + skb_get_queue_mapping(skb) >= dev->num_tx_queues) { *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE); } else { queue = skb_get_tx_queue(dev, skb); qdisc = rcu_dereference(queue->qdisc); + + spin_lock_bh(qdisc_lock(qdisc)); qdisc_qstats_qlen_backlog(qdisc, &qlen, &backlog); + spin_unlock_bh(qdisc_lock(qdisc)); *(__be32 *)data = cpu_to_be32(backlog); } From 5a37d228799b0ec2c277459c83c814a59d310bc3 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Sun, 5 Apr 2026 23:20:13 +0100 Subject: [PATCH 618/700] net: txgbe: leave space for null terminators on property_entry Lists of struct property_entry are supposed to be terminated with an empty property, this driver currently seems to be allocating exactly the amount of entry used. Change the struct definition to leave an extra element for all property_entry. Fixes: c3e382ad6d15 ("net: txgbe: Add software nodes to support phylink") Signed-off-by: Fabio Baltieri Tested-by: Jiawen Wu Link: https://patch.msgid.link/20260405222013.5347-1-fabio.baltieri@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 82433e9cb0e33e..6b05f32b4a0109 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -424,10 +424,10 @@ struct txgbe_nodes { char i2c_name[32]; char sfp_name[32]; char phylink_name[32]; - struct property_entry gpio_props[1]; - struct property_entry i2c_props[3]; - struct property_entry sfp_props[8]; - struct property_entry phylink_props[2]; + struct property_entry gpio_props[2]; + struct property_entry i2c_props[4]; + struct property_entry sfp_props[9]; + struct property_entry phylink_props[3]; struct software_node_ref_args i2c_ref[1]; struct software_node_ref_args gpio0_ref[1]; struct software_node_ref_args gpio1_ref[1]; From 9b55b253907e7431210483519c5ad711a37dafa1 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 6 Apr 2026 11:15:10 +0800 Subject: [PATCH 619/700] mptcp: fix slab-use-after-free in __inet_lookup_established The ehash table lookups are lockless and rely on SLAB_TYPESAFE_BY_RCU to guarantee socket memory stability during RCU read-side critical sections. Both tcp_prot and tcpv6_prot have their slab caches created with this flag via proto_register(). However, MPTCP's mptcp_subflow_init() copies tcpv6_prot into tcpv6_prot_override during inet_init() (fs_initcall, level 5), before inet6_init() (module_init/device_initcall, level 6) has called proto_register(&tcpv6_prot). At that point, tcpv6_prot.slab is still NULL, so tcpv6_prot_override.slab remains NULL permanently. This causes MPTCP v6 subflow child sockets to be allocated via kmalloc (falling into kmalloc-4k) instead of the TCPv6 slab cache. The kmalloc-4k cache lacks SLAB_TYPESAFE_BY_RCU, so when these sockets are freed without SOCK_RCU_FREE (which is cleared for child sockets by design), the memory can be immediately reused. Concurrent ehash lookups under rcu_read_lock can then access freed memory, triggering a slab-use-after-free in __inet_lookup_established. Fix this by splitting the IPv6-specific initialization out of mptcp_subflow_init() into a new mptcp_subflow_v6_init(), called from mptcp_proto_v6_init() before protocol registration. This ensures tcpv6_prot_override.slab correctly inherits the SLAB_TYPESAFE_BY_RCU slab cache. Fixes: b19bc2945b40 ("mptcp: implement delegated actions") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260406031512.189159-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 2 ++ net/mptcp/protocol.h | 1 + net/mptcp/subflow.c | 15 +++++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 65c3bb8016f47e..614c3f583ca030 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -4660,6 +4660,8 @@ int __init mptcp_proto_v6_init(void) { int err; + mptcp_subflow_v6_init(); + mptcp_v6_prot = mptcp_prot; strscpy(mptcp_v6_prot.name, "MPTCPv6", sizeof(mptcp_v6_prot.name)); mptcp_v6_prot.slab = NULL; diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 0bd1ee860316dd..ec15e503da8b70 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -875,6 +875,7 @@ static inline void mptcp_subflow_tcp_fallback(struct sock *sk, void __init mptcp_proto_init(void); #if IS_ENABLED(CONFIG_MPTCP_IPV6) int __init mptcp_proto_v6_init(void); +void __init mptcp_subflow_v6_init(void); #endif struct sock *mptcp_sk_clone_init(const struct sock *sk, diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 6716970693e9b7..4ff5863aa9fdeb 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -2165,7 +2165,15 @@ void __init mptcp_subflow_init(void) tcp_prot_override.psock_update_sk_prot = NULL; #endif + mptcp_diag_subflow_init(&subflow_ulp_ops); + + if (tcp_register_ulp(&subflow_ulp_ops) != 0) + panic("MPTCP: failed to register subflows to ULP\n"); +} + #if IS_ENABLED(CONFIG_MPTCP_IPV6) +void __init mptcp_subflow_v6_init(void) +{ /* In struct mptcp_subflow_request_sock, we assume the TCP request sock * structures for v4 and v6 have the same size. It should not changed in * the future but better to make sure to be warned if it is no longer @@ -2204,10 +2212,5 @@ void __init mptcp_subflow_init(void) /* Disable sockmap processing for subflows */ tcpv6_prot_override.psock_update_sk_prot = NULL; #endif -#endif - - mptcp_diag_subflow_init(&subflow_ulp_ops); - - if (tcp_register_ulp(&subflow_ulp_ops) != 0) - panic("MPTCP: failed to register subflows to ULP\n"); } +#endif From 8e2760eaab778494fc1fa257031e0e1799647f46 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Tue, 7 Apr 2026 10:41:41 +0200 Subject: [PATCH 620/700] Revert "mptcp: add needs_id for netlink appending addr" This commit was originally adding the ability to add MPTCP endpoints with ID 0 by accident. The in-kernel PM, handling MPTCP endpoints at the net namespace level, is not supposed to handle endpoints with such ID, because this ID 0 is reserved to the initial subflow, as mentioned in the MPTCPv1 protocol [1], a per-connection setting. Note that 'ip mptcp endpoint add id 0' stops early with an error, but other tools might still request the in-kernel PM to create MPTCP endpoints with this restricted ID 0. In other words, it was wrong to call the mptcp_pm_has_addr_attr_id helper to check whether the address ID attribute is set: if it was set to 0, a new MPTCP endpoint would be created with ID 0, which is not expected, and might cause various issues later. Fixes: 584f38942626 ("mptcp: add needs_id for netlink appending addr") Cc: stable@vger.kernel.org Link: https://datatracker.ietf.org/doc/html/rfc8684#section-3.2-9 [1] Reviewed-by: Geliang Tang Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260407-net-mptcp-revert-pm-needs-id-v2-1-7a25cbc324f8@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm_kernel.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index 82e59f9c6dd9ce..0ebf43be993993 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -720,7 +720,7 @@ static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry) static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, struct mptcp_pm_addr_entry *entry, - bool needs_id, bool replace) + bool replace) { struct mptcp_pm_addr_entry *cur, *del_entry = NULL; int ret = -EINVAL; @@ -779,7 +779,7 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, } } - if (!entry->addr.id && needs_id) { + if (!entry->addr.id) { find_next: entry->addr.id = find_next_zero_bit(pernet->id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1, @@ -790,7 +790,7 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, } } - if (!entry->addr.id && needs_id) + if (!entry->addr.id) goto out; __set_bit(entry->addr.id, pernet->id_bitmap); @@ -923,7 +923,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, return -ENOMEM; entry->addr.port = 0; - ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true, false); + ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, false); if (ret < 0) kfree(entry); @@ -977,18 +977,6 @@ static int mptcp_nl_add_subflow_or_signal_addr(struct net *net, return 0; } -static bool mptcp_pm_has_addr_attr_id(const struct nlattr *attr, - struct genl_info *info) -{ - struct nlattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1]; - - if (!nla_parse_nested_deprecated(tb, MPTCP_PM_ADDR_ATTR_MAX, attr, - mptcp_pm_address_nl_policy, info->extack) && - tb[MPTCP_PM_ADDR_ATTR_ID]) - return true; - return false; -} - /* Add an MPTCP endpoint */ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info) { @@ -1037,9 +1025,7 @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info) goto out_free; } } - ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, - !mptcp_pm_has_addr_attr_id(attr, info), - true); + ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true); if (ret < 0) { GENL_SET_ERR_MSG_FMT(info, "too many addresses or duplicate one: %d", ret); goto out_free; From 39897df386376912d561d4946499379effa1e7ef Mon Sep 17 00:00:00 2001 From: Jiexun Wang Date: Tue, 7 Apr 2026 16:00:14 +0800 Subject: [PATCH 621/700] af_unix: read UNIX_DIAG_VFS data under unix_state_lock Exact UNIX diag lookups hold a reference to the socket, but not to u->path. Meanwhile, unix_release_sock() clears u->path under unix_state_lock() and drops the path reference after unlocking. Read the inode and device numbers for UNIX_DIAG_VFS while holding unix_state_lock(), then emit the netlink attribute after dropping the lock. This keeps the VFS data stable while the reply is being built. Fixes: 5f7b0569460b ("unix_diag: Unix inode info NLA") Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Jiexun Wang Signed-off-by: Ren Wei Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260407080015.1744197-1-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/unix/diag.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/net/unix/diag.c b/net/unix/diag.c index ca34730261510c..c9c1e51c441969 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -28,18 +28,23 @@ static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb) static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb) { - struct dentry *dentry = unix_sk(sk)->path.dentry; + struct unix_diag_vfs uv; + struct dentry *dentry; + bool have_vfs = false; + unix_state_lock(sk); + dentry = unix_sk(sk)->path.dentry; if (dentry) { - struct unix_diag_vfs uv = { - .udiag_vfs_ino = d_backing_inode(dentry)->i_ino, - .udiag_vfs_dev = dentry->d_sb->s_dev, - }; - - return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv); + uv.udiag_vfs_ino = d_backing_inode(dentry)->i_ino; + uv.udiag_vfs_dev = dentry->d_sb->s_dev; + have_vfs = true; } + unix_state_unlock(sk); - return 0; + if (!have_vfs) + return 0; + + return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv); } static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb) From 0006c6f1091bbeea88b8a88a6548b9fb2f803c74 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 6 Apr 2026 22:27:30 -0400 Subject: [PATCH 622/700] devlink: Fix incorrect skb socket family dumping The devlink_fmsg_dump_skb function was incorrectly using the socket type (sk->sk_type) instead of the socket family (sk->sk_family) when filling the "family" field in the fast message dump. This patch fixes this to properly display the socket family. Fixes: 3dbfde7f6bc7b8 ("devlink: add devlink_fmsg_dump_skb() function") Signed-off-by: Li RongQing Link: https://patch.msgid.link/20260407022730.2393-1-lirongqing@baidu.com Signed-off-by: Jakub Kicinski --- net/devlink/health.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/devlink/health.c b/net/devlink/health.c index 449c7611c640f1..ea7a334e939b57 100644 --- a/net/devlink/health.c +++ b/net/devlink/health.c @@ -1327,7 +1327,7 @@ void devlink_fmsg_dump_skb(struct devlink_fmsg *fmsg, const struct sk_buff *skb) if (sk) { devlink_fmsg_pair_nest_start(fmsg, "sk"); devlink_fmsg_obj_nest_start(fmsg); - devlink_fmsg_put(fmsg, "family", sk->sk_type); + devlink_fmsg_put(fmsg, "family", sk->sk_family); devlink_fmsg_put(fmsg, "type", sk->sk_type); devlink_fmsg_put(fmsg, "proto", sk->sk_protocol); devlink_fmsg_obj_nest_end(fmsg); From 30f3b767aed45fdaf8f887e66e1f19bd0cbd4483 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Mon, 6 Apr 2026 13:08:16 +0530 Subject: [PATCH 623/700] MAINTAINERS: Add Prashanth as additional maintainer for amd-xgbe driver Add Prashanth as an additional maintainer for the amd-xgbe Ethernet driver to help with ongoing development and maintenance. Cc: Prashanth Kumar K R Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20260406073816.3218387-1-Raju.Rangoju@amd.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4601903a8f9b34..dc935838e51619 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1291,6 +1291,7 @@ F: include/uapi/drm/amdxdna_accel.h AMD XGBE DRIVER M: Raju Rangoju +M: Prashanth Kumar K R L: netdev@vger.kernel.org S: Maintained F: arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi From de65275fc94e2e0acc79bd016d60889bf251ccd9 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 9 Apr 2026 10:40:28 +0800 Subject: [PATCH 624/700] ALSA: hda/realtek: Add quirk for CSL Unity BF24B The CSL Unity BF24B all-in-one PC uses a Realtek ALC662 rev3 audio codec and requires the correct GPIO configuration to enable sound output from both the speakers and the headphone. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221258 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260409024028.1297587-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc662.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 5073165d1f3cf9..3abe41c7315c46 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -255,6 +255,25 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +static void alc662_fixup_csl_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gpio_mask |= 0x03; + spec->gpio_dir |= 0x03; + break; + case HDA_FIXUP_ACT_INIT: + /* need to toggle GPIO to enable the amp */ + alc_update_gpio_data(codec, 0x03, true); + msleep(100); + alc_update_gpio_data(codec, 0x03, false); + break; + } +} + enum { ALC662_FIXUP_ASPIRE, ALC662_FIXUP_LED_GPIO1, @@ -313,6 +332,7 @@ enum { ALC897_FIXUP_HEADSET_MIC_PIN2, ALC897_FIXUP_UNIS_H3C_X500S, ALC897_FIXUP_HEADSET_MIC_PIN3, + ALC662_FIXUP_CSL_GPIO, }; static const struct hda_fixup alc662_fixups[] = { @@ -766,11 +786,16 @@ static const struct hda_fixup alc662_fixups[] = { { } }, }, + [ALC662_FIXUP_CSL_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_csl_amp, + }, }; static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), + SND_PCI_QUIRK(0x1022, 0xc950, "CSL Unity BF24B", ALC662_FIXUP_CSL_GPIO), SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), From 9709b56d908acc120fe8b4ae250b3c9d749ea832 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Fri, 3 Apr 2026 18:43:47 +0200 Subject: [PATCH 625/700] net: ipa: fix GENERIC_CMD register field masks for IPA v5.0+ Fix the field masks to match the hardware layout documented in downstream GSI (GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_*). Notably this fixes a WARN I was seeing when I tried to send "stop" to the MPSS remoteproc while IPA was up. Fixes: faf0678ec8a0 ("net: ipa: add IPA v5.0 GSI register definitions") Signed-off-by: Alexander Koskovich Signed-off-by: Luca Weiss Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260403-milos-ipa-v1-1-01e9e4e03d3e@fairphone.com Signed-off-by: Paolo Abeni --- drivers/net/ipa/reg/gsi_reg-v5.0.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipa/reg/gsi_reg-v5.0.c b/drivers/net/ipa/reg/gsi_reg-v5.0.c index 36d1e65df71bb1..3334d8e20ad28b 100644 --- a/drivers/net/ipa/reg/gsi_reg-v5.0.c +++ b/drivers/net/ipa/reg/gsi_reg-v5.0.c @@ -156,9 +156,10 @@ REG_FIELDS(EV_CH_CMD, ev_ch_cmd, 0x00025010 + 0x12000 * GSI_EE_AP); static const u32 reg_generic_cmd_fmask[] = { [GENERIC_OPCODE] = GENMASK(4, 0), - [GENERIC_CHID] = GENMASK(9, 5), - [GENERIC_EE] = GENMASK(13, 10), - /* Bits 14-31 reserved */ + [GENERIC_CHID] = GENMASK(12, 5), + [GENERIC_EE] = GENMASK(16, 13), + /* Bits 17-23 reserved */ + [GENERIC_PARAMS] = GENMASK(31, 24), }; REG_FIELDS(GENERIC_CMD, generic_cmd, 0x00025018 + 0x12000 * GSI_EE_AP); From 56007972c0b1e783ca714d6f1f4d6e66e531d21f Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Fri, 3 Apr 2026 18:43:48 +0200 Subject: [PATCH 626/700] net: ipa: fix event ring index not programmed for IPA v5.0+ For IPA v5.0+, the event ring index field moved from CH_C_CNTXT_0 to CH_C_CNTXT_1. The v5.0 register definition intended to define this field in the CH_C_CNTXT_1 fmask array but used the old identifier of ERINDEX instead of CH_ERINDEX. Without a valid event ring, GSI channels could never signal transfer completions. This caused gsi_channel_trans_quiesce() to block forever in wait_for_completion(). At least for IPA v5.2 this resolves an issue seen where runtime suspend, system suspend, and remoteproc stop all hanged forever. It also meant the IPA data path was completely non functional. Fixes: faf0678ec8a0 ("net: ipa: add IPA v5.0 GSI register definitions") Signed-off-by: Alexander Koskovich Signed-off-by: Luca Weiss Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260403-milos-ipa-v1-2-01e9e4e03d3e@fairphone.com Signed-off-by: Paolo Abeni --- drivers/net/ipa/reg/gsi_reg-v5.0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ipa/reg/gsi_reg-v5.0.c b/drivers/net/ipa/reg/gsi_reg-v5.0.c index 3334d8e20ad28b..6c4a7fbe4de949 100644 --- a/drivers/net/ipa/reg/gsi_reg-v5.0.c +++ b/drivers/net/ipa/reg/gsi_reg-v5.0.c @@ -30,7 +30,7 @@ REG_STRIDE_FIELDS(CH_C_CNTXT_0, ch_c_cntxt_0, static const u32 reg_ch_c_cntxt_1_fmask[] = { [CH_R_LENGTH] = GENMASK(23, 0), - [ERINDEX] = GENMASK(31, 24), + [CH_ERINDEX] = GENMASK(31, 24), }; REG_STRIDE_FIELDS(CH_C_CNTXT_1, ch_c_cntxt_1, From ebe560ea5f54134279356703e73b7f867c89db13 Mon Sep 17 00:00:00 2001 From: Alice Mikityanska Date: Fri, 3 Apr 2026 20:49:49 +0300 Subject: [PATCH 627/700] l2tp: Drop large packets with UDP encap syzbot reported a WARN on my patch series [1]. The actual issue is an overflow of 16-bit UDP length field, and it exists in the upstream code. My series added a debug WARN with an overflow check that exposed the issue, that's why syzbot tripped on my patches, rather than on upstream code. syzbot's repro: r0 = socket$pppl2tp(0x18, 0x1, 0x1) r1 = socket$inet6_udp(0xa, 0x2, 0x0) connect$inet6(r1, &(0x7f00000000c0)={0xa, 0x0, 0x0, @loopback, 0xfffffffc}, 0x1c) connect$pppl2tp(r0, &(0x7f0000000240)=@pppol2tpin6={0x18, 0x1, {0x0, r1, 0x4, 0x0, 0x0, 0x0, {0xa, 0x4e22, 0xffff, @ipv4={'\x00', '\xff\xff', @empty}}}}, 0x32) writev(r0, &(0x7f0000000080)=[{&(0x7f0000000000)="ee", 0x34000}], 0x1) It basically sends an oversized (0x34000 bytes) PPPoL2TP packet with UDP encapsulation, and l2tp_xmit_core doesn't check for overflows when it assigns the UDP length field. The value gets trimmed to 16 bites. Add an overflow check that drops oversized packets and avoids sending packets with trimmed UDP length to the wire. syzbot's stack trace (with my patch applied): len >= 65536u WARNING: ./include/linux/udp.h:38 at udp_set_len_short include/linux/udp.h:38 [inline], CPU#1: syz.0.17/5957 WARNING: ./include/linux/udp.h:38 at l2tp_xmit_core net/l2tp/l2tp_core.c:1293 [inline], CPU#1: syz.0.17/5957 WARNING: ./include/linux/udp.h:38 at l2tp_xmit_skb+0x1204/0x18d0 net/l2tp/l2tp_core.c:1327, CPU#1: syz.0.17/5957 Modules linked in: CPU: 1 UID: 0 PID: 5957 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:udp_set_len_short include/linux/udp.h:38 [inline] RIP: 0010:l2tp_xmit_core net/l2tp/l2tp_core.c:1293 [inline] RIP: 0010:l2tp_xmit_skb+0x1204/0x18d0 net/l2tp/l2tp_core.c:1327 Code: 0f 0b 90 e9 21 f9 ff ff e8 e9 05 ec f6 90 0f 0b 90 e9 8d f9 ff ff e8 db 05 ec f6 90 0f 0b 90 e9 cc f9 ff ff e8 cd 05 ec f6 90 <0f> 0b 90 e9 de fa ff ff 44 89 f1 80 e1 07 80 c1 03 38 c1 0f 8c 4f RSP: 0018:ffffc90003d67878 EFLAGS: 00010293 RAX: ffffffff8ad985e3 RBX: ffff8881a6400090 RCX: ffff8881697f0000 RDX: 0000000000000000 RSI: 0000000000034010 RDI: 000000000000ffff RBP: dffffc0000000000 R08: 0000000000000003 R09: 0000000000000004 R10: dffffc0000000000 R11: fffff520007acf00 R12: ffff8881baf20900 R13: 0000000000034010 R14: ffff8881a640008e R15: ffff8881760f7000 FS: 000055557e81f500(0000) GS:ffff8882a9467000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000200000033000 CR3: 00000001612f4000 CR4: 00000000000006f0 Call Trace: pppol2tp_sendmsg+0x40a/0x5f0 net/l2tp/l2tp_ppp.c:302 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] sock_write_iter+0x503/0x550 net/socket.c:1195 do_iter_readv_writev+0x619/0x8c0 fs/read_write.c:-1 vfs_writev+0x33c/0x990 fs/read_write.c:1059 do_writev+0x154/0x2e0 fs/read_write.c:1105 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f636479c629 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffffd4241c8 EFLAGS: 00000246 ORIG_RAX: 0000000000000014 RAX: ffffffffffffffda RBX: 00007f6364a15fa0 RCX: 00007f636479c629 RDX: 0000000000000001 RSI: 0000200000000080 RDI: 0000000000000003 RBP: 00007f6364832b39 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f6364a15fac R14: 00007f6364a15fa0 R15: 00007f6364a15fa0 [1]: https://lore.kernel.org/all/20260226201600.222044-1-alice.kernel@fastmail.im/ Fixes: 3557baabf280 ("[L2TP]: PPP over L2TP driver core") Reported-by: syzbot+ci3edea60a44225dec@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/69a1dfba.050a0220.3a55be.0026.GAE@google.com/ Signed-off-by: Alice Mikityanska Link: https://patch.msgid.link/20260403174949.843941-1-alice.kernel@fastmail.im Signed-off-by: Paolo Abeni --- net/l2tp/l2tp_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index c89ae52764b892..157fc23ce4e14e 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1290,6 +1290,11 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, uns uh->source = inet->inet_sport; uh->dest = inet->inet_dport; udp_len = uhlen + session->hdr_len + data_len; + if (udp_len > U16_MAX) { + kfree_skb(skb); + ret = NET_XMIT_DROP; + goto out_unlock; + } uh->len = htons(udp_len); /* Calculate UDP checksum if configured to do so */ From 1561d96f5f55c1bca9ff047ace5813f4f244eea6 Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Tue, 7 Apr 2026 14:02:47 -0700 Subject: [PATCH 628/700] gpio: tegra: fix irq_release_resources calling enable instead of disable tegra_gpio_irq_release_resources() erroneously calls tegra_gpio_enable() instead of tegra_gpio_disable(). When IRQ resources are released, the GPIO configuration bit (CNF) should be cleared to deconfigure the pin as a GPIO. Leaving it enabled wastes power and can cause unexpected behavior if the pin is later reused for an alternate function via pinctrl. Fixes: 66fecef5bde0 ("gpio: tegra: Convert to gpio_irq_chip") Signed-off-by: Samasth Norway Ananda Link: https://patch.msgid.link/20260407210247.1737938-1-samasth.norway.ananda@oracle.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 15a5762a82c25b..b14052fe64ac69 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -595,7 +595,7 @@ static void tegra_gpio_irq_release_resources(struct irq_data *d) struct tegra_gpio_info *tgi = gpiochip_get_data(chip); gpiochip_relres_irq(chip, d->hwirq); - tegra_gpio_enable(tgi, d->hwirq); + tegra_gpio_disable(tgi, d->hwirq); } static void tegra_gpio_irq_print_chip(struct irq_data *d, struct seq_file *s) From a13edf9b92fc4700b3020d7ea547a3d64dd33b63 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Thu, 9 Apr 2026 08:31:11 +0300 Subject: [PATCH 629/700] drm/i915/gem: Drop check for changed VM in EXECBUF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the introduction of d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") it has not been possible for VM to change after context creation so the check will never fail. Sima's analysis: This check was added in f7ce8639f6ff ("drm/i915/gem: Split the context's obj:vma lut into its own mutex") but without any hint in the commit message as to why. In another hunk of that commit there's a hint though in __eb_add_lut: /* user racing with ctx set-vm */ This would mean that this bug was introduced in e0695db7298e ("drm/i915: Create/destroy VM (ppGTT) for use with contexts"), which allowed to change the gem_ctx->vm at runtime, opening up the race that was partially fixed in the earlier referenced commit about a year later. But it cannot be exploited anymore in anything remotely recent because with the introduction of proto-contexts we've made gem_ctx->vm invariant again, exactly to preemptively close all these potential issues. Specifically d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") is the vm specific part of the proto-context work. v3: - Include Sima's analysis and WARN_ON_ONCE v4: - Focus only on latest mainline codebase References: https://lore.kernel.org/all/20260324151741.29338-1-sosohero200@gmail.com/ Reported-by: Ville Syrjälä Cc: Linus Torvalds Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Andi Shyti Signed-off-by: Joonas Lahtinen Reviewed-by: Tvrtko Ursulin Link: https://patch.msgid.link/20260409053111.8914-1-joonas.lahtinen@linux.intel.com (cherry picked from commit f6d4afc9ec6a0bc326151b35a7a3369369180079) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 942f4eed817f3c..65ce54b20ec299 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -896,7 +896,7 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle) rcu_read_lock(); vma = radix_tree_lookup(&eb->gem_context->handles_vma, handle); - if (likely(vma && vma->vm == vm)) + if (likely(vma)) vma = i915_vma_tryget(vma); else vma = NULL; From aa6c1052b7730e18d5999f9a5cfb1dadaac82310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 8 Apr 2026 12:17:37 -0300 Subject: [PATCH 630/700] ALSA: i2c: ak4xxx-adda: seed AK5365 cache with reset defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_akm4xxx_init() clears the register and volume caches before dispatching by codec type. The AK5365 case then returns immediately, leaving the software cache at zero instead of the documented AK5365 reset defaults. The AK5365 capture volume controls read from volumes[] and the proc register dump reads from images[], so the initial capture volume state and proc output are wrong until a control write happens. Seed the AK5365 cache with its documented reset defaults instead of adding a guessed init sequence. The datasheet documents the reset values and states that MCLK/LRCK changes do not require a PDN/PWN reset because the chip has a built-in reset-free circuit. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260408-ak5365-cache-defaults-v1-1-fff639aca3e3@gmail.com Signed-off-by: Takashi Iwai --- sound/i2c/other/ak4xxx-adda.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index bdeb3cef3640e1..9dd36b82a6ac47 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -281,6 +281,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) 0x07, 0x00, /* 7: ROUT muted */ 0xff, 0xff }; + static const unsigned char ak5365_defaults[] = { + 0x01, 0x00, 0x00, 0x2b, 0x7f, 0x7f, 0x28, 0x89, + }; int chip; const unsigned char *ptr, *inits; @@ -327,10 +330,12 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) ak->total_regs = 0x05; break; case SND_AK5365: - /* FIXME: any init sequence? */ ak->num_chips = 1; ak->name = "ak5365"; ak->total_regs = 0x08; + memcpy(ak->images, ak5365_defaults, sizeof(ak5365_defaults)); + snd_akm4xxx_set_vol(ak, 0, 0x04, 127); + snd_akm4xxx_set_vol(ak, 0, 0x05, 127); return; case SND_AK4620: inits = inits_ak4620; From 49690509ebdcbfa7618dd5a5ff3c89f7af9a5b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 9 Apr 2026 02:07:45 -0300 Subject: [PATCH 631/700] ALSA: msnd: prepare system sleep support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System suspend cannot work for msnd today because the PCM trigger paths reject SNDRV_PCM_TRIGGER_SUSPEND, and the driver has only refcounted IRQ helpers. Add the small helpers needed by the PM callbacks and restore master volume from the cached ALSA mixer state when the DSP is reinitialized. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260409-msnd-pm-support-v1-1-2abef720d0e7@gmail.com Signed-off-by: Takashi Iwai --- sound/isa/msnd/msnd.c | 72 +++++++++++++++++++--------- sound/isa/msnd/msnd.h | 1 + sound/isa/msnd/msnd_pinnacle_mixer.c | 4 ++ 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c index 5e350234d5726b..77367e102fda2b 100644 --- a/sound/isa/msnd/msnd.c +++ b/sound/isa/msnd/msnd.c @@ -127,11 +127,8 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len) } EXPORT_SYMBOL(snd_msnd_upload_host); -int snd_msnd_enable_irq(struct snd_msnd *dev) +static int __snd_msnd_enable_irq(struct snd_msnd *dev) { - if (dev->irq_ref++) - return 0; - dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n"); guard(spinlock_irqsave)(&dev->lock); @@ -152,17 +149,9 @@ int snd_msnd_enable_irq(struct snd_msnd *dev) return -EIO; } -EXPORT_SYMBOL(snd_msnd_enable_irq); -int snd_msnd_disable_irq(struct snd_msnd *dev) +static int __snd_msnd_disable_irq(struct snd_msnd *dev) { - if (--dev->irq_ref > 0) - return 0; - - if (dev->irq_ref < 0) - dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n", - dev->irq_ref); - dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n"); guard(spinlock_irqsave)(&dev->lock); @@ -178,8 +167,39 @@ int snd_msnd_disable_irq(struct snd_msnd *dev) return -EIO; } + +int snd_msnd_enable_irq(struct snd_msnd *dev) +{ + if (dev->irq_ref++) + return 0; + + return __snd_msnd_enable_irq(dev); +} +EXPORT_SYMBOL(snd_msnd_enable_irq); + +int snd_msnd_disable_irq(struct snd_msnd *dev) +{ + if (--dev->irq_ref > 0) + return 0; + + if (dev->irq_ref < 0) + dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n", + dev->irq_ref); + + return __snd_msnd_disable_irq(dev); +} EXPORT_SYMBOL(snd_msnd_disable_irq); +int snd_msnd_force_irq(struct snd_msnd *dev, bool enable) +{ + if (!dev->irq_ref) + return 0; + + return enable ? __snd_msnd_enable_irq(dev) : + __snd_msnd_disable_irq(dev); +} +EXPORT_SYMBOL(snd_msnd_force_irq); + static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size) { long tmp = (size * HZ * chip->play_sample_size) / 8; @@ -507,25 +527,27 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_msnd *chip = snd_pcm_substream_chip(substream); - int result = 0; - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: dev_dbg(chip->card->dev, "%s(START)\n", __func__); chip->banksPlayed = 0; set_bit(F_WRITING, &chip->flags); snd_msnd_DAPQ(chip, 1); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: dev_dbg(chip->card->dev, "%s(STOP)\n", __func__); - /* interrupt diagnostic, comment this out later */ clear_bit(F_WRITING, &chip->flags); snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); - } else { + break; + default: dev_dbg(chip->card->dev, "%s(?????)\n", __func__); - result = -EINVAL; + return -EINVAL; } dev_dbg(chip->card->dev, "%s() ENDE\n", __func__); - return result; + return 0; } static snd_pcm_uframes_t @@ -589,17 +611,22 @@ static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream, { struct snd_msnd *chip = snd_pcm_substream_chip(substream); - if (cmd == SNDRV_PCM_TRIGGER_START) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: chip->last_recbank = -1; set_bit(F_READING, &chip->flags); if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0) return 0; clear_bit(F_READING, &chip->flags); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: clear_bit(F_READING, &chip->flags); snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); return 0; + default: + break; } return -EINVAL; } @@ -668,4 +695,3 @@ EXPORT_SYMBOL(snd_msnd_pcm); MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers"); MODULE_LICENSE("GPL"); - diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h index 3d7810ed9186bf..b25beca25c0dda 100644 --- a/sound/isa/msnd/msnd.h +++ b/sound/isa/msnd/msnd.h @@ -280,6 +280,7 @@ int snd_msnd_upload_host(struct snd_msnd *chip, const u8 *bin, int len); int snd_msnd_enable_irq(struct snd_msnd *chip); int snd_msnd_disable_irq(struct snd_msnd *chip); +int snd_msnd_force_irq(struct snd_msnd *chip, bool enable); void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file); int snd_msnd_DAPQ(struct snd_msnd *chip, int start); int snd_msnd_DARQ(struct snd_msnd *chip, int start); diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index ec354483b9f825..8ca98722175389 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -310,6 +310,10 @@ EXPORT_SYMBOL(snd_msndmix_new); void snd_msndmix_setup(struct snd_msnd *dev) { + writew(dev->left_levels[MSND_MIXER_VOLUME], + dev->SMA + SMA_wCurrMastVolLeft); + writew(dev->right_levels[MSND_MIXER_VOLUME], + dev->SMA + SMA_wCurrMastVolRight); update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); update_volm(MSND_MIXER_PCM, wCurrPlayVol); From efca489a86fc6a5364215fdf03c2fad3b864d03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 9 Apr 2026 02:07:46 -0300 Subject: [PATCH 632/700] ALSA: msnd: add ISA and PnP system sleep callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The msnd drivers do not implement system sleep callbacks today, so they have no defined way to recover DSP state after suspend. Add common card suspend/resume helpers, rerun the DSP initialization path on resume, restore the cached capture-source state, and rearm the shared IRQ for already-open users. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260409-msnd-pm-support-v1-2-2abef720d0e7@gmail.com Signed-off-by: Takashi Iwai --- sound/isa/msnd/msnd.h | 2 + sound/isa/msnd/msnd_pinnacle.c | 95 +++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h index b25beca25c0dda..56a700e6a5cbf5 100644 --- a/sound/isa/msnd/msnd.h +++ b/sound/isa/msnd/msnd.h @@ -253,6 +253,8 @@ struct snd_msnd { spinlock_t mixer_lock; int nresets; unsigned recsrc; + u8 pm_recsrc; + bool pm_mpu_input; #define LEVEL_ENTRIES 32 int left_levels[LEVEL_ENTRIES]; int right_levels[LEVEL_ENTRIES]; diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index c4eec391cd298c..5b729bb02ef628 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -513,6 +513,19 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu) snd_msnd_disable_irq(mpu->private_data); } +#ifdef CONFIG_PM +static u8 snd_msnd_pm_recsrc(struct snd_msnd *chip) +{ + /* Convert recsrc to the Capture Source selector: 0=Analog, 1=MASS, 2=SPDIF. */ + if (chip->recsrc & BIT(4)) + return 1; + if ((chip->recsrc & BIT(17)) && + test_bit(F_HAVEDIGITAL, &chip->flags)) + return 2; + return 0; +} +#endif + static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; @@ -1001,10 +1014,73 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx) return 0; } +#ifdef CONFIG_PM +static int snd_msnd_card_suspend(struct snd_card *card) +{ + struct snd_msnd *chip = card->private_data; + struct snd_mpu401 *mpu; + int err; + + mpu = chip->rmidi ? chip->rmidi->private_data : NULL; + chip->pm_recsrc = snd_msnd_pm_recsrc(chip); + chip->pm_mpu_input = mpu && test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_STOP); + + err = snd_msnd_force_irq(chip, false); + if (err < 0) { + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START); + return err; + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + return 0; +} + +static int snd_msnd_card_resume(struct snd_card *card) +{ + struct snd_msnd *chip = card->private_data; + int err; + + err = snd_msnd_initialize(card); + if (err < 0) + return err; + + snd_msnd_calibrate_adc(chip, chip->play_sample_rate); + snd_msndmix_force_recsrc(chip, chip->pm_recsrc); + + err = snd_msnd_force_irq(chip, true); + if (err < 0) + return err; + + if (chip->pm_mpu_input) + snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START); + + chip->nresets = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_msnd_isa_suspend(struct device *dev, unsigned int idx, + pm_message_t state) +{ + return snd_msnd_card_suspend(dev_get_drvdata(dev)); +} + +static int snd_msnd_isa_resume(struct device *dev, unsigned int idx) +{ + return snd_msnd_card_resume(dev_get_drvdata(dev)); +} +#endif + static struct isa_driver snd_msnd_driver = { .match = snd_msnd_isa_match, .probe = snd_msnd_isa_probe, - /* FIXME: suspend, resume */ +#ifdef CONFIG_PM + .suspend = snd_msnd_isa_suspend, + .resume = snd_msnd_isa_resume, +#endif .driver = { .name = DEV_NAME }, @@ -1111,6 +1187,18 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int snd_msnd_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) +{ + return snd_msnd_card_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_msnd_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_msnd_card_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static int isa_registered; static int pnp_registered; @@ -1127,6 +1215,10 @@ static struct pnp_card_driver msnd_pnpc_driver = { .name = "msnd_pinnacle", .id_table = msnd_pnpids, .probe = snd_msnd_pnp_detect, +#ifdef CONFIG_PM + .suspend = snd_msnd_pnp_suspend, + .resume = snd_msnd_pnp_resume, +#endif }; #endif /* CONFIG_PNP */ @@ -1161,4 +1253,3 @@ static void __exit snd_msnd_exit(void) module_init(snd_msnd_init); module_exit(snd_msnd_exit); - From 9575766a682f50ec4bcb85ecd438685bdc09f9cc Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Thu, 9 Apr 2026 08:21:37 +0000 Subject: [PATCH 633/700] ALSA: usb-audio: Add iface reset and delay quirk for HUAWEI USB-C HEADSET Setting up the interface when suspended/resumeing fail on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: new full-speed USB device number 2 using xhci-hcd usb 1-1: New USB device found, idVendor=12d1, idProduct=3a07 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: HUAWEI USB-C HEADSET usb 1-1: Manufacturer: bestechnic usb 1-1: SerialNumber: 0296C100000000000000000000000 Signed-off-by: Lianqin Hu Link: https://patch.msgid.link/TYUPR06MB62176A18EA7A9DD0AC2826BCD2582@TYUPR06MB6217.apcprd06.prod.outlook.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 48acd8dac689e1..6cf85922b7bedf 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2347,8 +2347,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), + DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE | + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ From b0762dd2fcab5b8b4b953314f3f6eb9d92bc16bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:14 +0200 Subject: [PATCH 634/700] ALSA: hda: Add sync version of snd_hda_codec_write() We used snd_hda_codec_read() for the verb write when a synchronization is needed after the write, e.g. for the power state toggle or such cases. It works in principle, but it looks rather confusing and too hackish. For improving the code readability, introduce a new helper function, snd_hda_codec_write_sync(), which is another variant of snd_hda_codec_write(), and replace the existing snd_hda_codec_read() calls with this one. No behavior change but just the code refactoring. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-2-tiwai@suse.de --- include/sound/hda_codec.h | 11 +++++++++++ sound/hda/codecs/generic.c | 2 +- sound/hda/codecs/hdmi/intelhdmi.c | 2 +- sound/hda/codecs/realtek/realtek.c | 3 +-- sound/hda/codecs/sigmatel.c | 4 ++-- sound/hda/common/codec.c | 11 +++++------ 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5d9f0ef228af10..292d6024388b3e 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -336,6 +336,17 @@ snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm); } +/* sync after write */ +static inline int +snd_hda_codec_write_sync(struct hda_codec *codec, hda_nid_t nid, int flags, + unsigned int verb, unsigned int parm) +{ + /* use snd_hda_codec_read() for writing; + * the returned value is usually discarded + */ + return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm); +} + #define snd_hda_param_read(codec, nid, param) \ snd_hdac_read_parm(&(codec)->core, nid, param) #define snd_hda_get_sub_nodes(codec, nid, start_nid) \ diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 092428ada29d3e..660a9f2c0dedcf 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -863,7 +863,7 @@ static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) { if (nid) { msleep(10); - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + snd_hda_codec_write_sync(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); } } diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 9460c8db39a93e..6a7882544ab776 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -85,7 +85,7 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, } } - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index b240f13b043872..39a1ead3b74366 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -411,9 +411,8 @@ void alc_headset_mic_no_shutup(struct hda_codec *codec) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, + snd_hda_codec_write_sync(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c index acbbc7c3508be1..4ff80a65168fdb 100644 --- a/sound/hda/codecs/sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -311,12 +311,12 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, fg, 0, + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_read(codec, fg, 0, + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 5123df32ad89f5..3ac4bf6005d60d 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -606,9 +606,8 @@ void snd_hda_shutup_pins(struct hda_codec *codec) if (codec->bus->shutdown) return; snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write_sync(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } codec->pins_shutup = 1; } @@ -2794,9 +2793,9 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, if (codec->power_filter) state = codec->power_filter(codec, fg, state); if (state == power_state || power_state != AC_PWRST_D3) - snd_hda_codec_read(codec, fg, flags, - AC_VERB_SET_POWER_STATE, - state); + snd_hda_codec_write_sync(codec, fg, flags, + AC_VERB_SET_POWER_STATE, + state); snd_hda_codec_set_power_to_all(codec, fg, power_state); } state = snd_hda_sync_power_state(codec, fg, power_state); From cd8fd5a0566e0d93fbd408e6b06ca484a78b5ccd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:15 +0200 Subject: [PATCH 635/700] ALSA: hda: Add a simple GPIO setup helper function Introduce a common GPIO setup helper function, so that we can clean up the open code found in many codec drivers later. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-3-tiwai@suse.de --- include/sound/hda_codec.h | 4 ++++ sound/hda/common/codec.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 292d6024388b3e..24581080e26a04 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -481,6 +481,10 @@ void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_bus_reset(struct hda_bus *bus); void snd_hda_bus_reset_codecs(struct hda_bus *bus); +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay); + int snd_hda_codec_set_name(struct hda_codec *codec, const char *name); /* diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 3ac4bf6005d60d..c2af2511a83160 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -4052,6 +4052,35 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus) } } +/** + * snd_hda_codec_set_gpio - Set up GPIO bits for AFG + * @codec: the HDA codec + * @mask: GPIO bitmask + * @dir: GPIO direction bits + * @data: GPIO data bits + * @delay: the delay in msec before writing GPIO data bits + */ +void snd_hda_codec_set_gpio(struct hda_codec *codec, unsigned int mask, + unsigned int dir, unsigned int data, + unsigned int delay) +{ + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, mask); + if (delay) { + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + msleep(delay); + snd_hda_codec_write_sync(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } else { + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, data); + } +} +EXPORT_SYMBOL_GPL(snd_hda_codec_set_gpio); + /** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer * @pcm: PCM caps bits From d19ecd85a245a2052a502f72bee83982f535c2e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:16 +0200 Subject: [PATCH 636/700] ALSA: hda/realtek: Clean up with snd_hda_codec_set_gpio() Use a new helper function to clean up the code. Along with it, make alc_write_gpio() static as well, which is used only locally in realtek.c. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-4-tiwai@suse.de --- sound/hda/codecs/realtek/realtek.c | 13 ++++--------- sound/hda/codecs/realtek/realtek.h | 1 - 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index 39a1ead3b74366..d9b2f1993eaf73 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -123,22 +123,17 @@ void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, } EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK"); -void alc_write_gpio(struct hda_codec *codec) +static void alc_write_gpio(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; if (!spec->gpio_mask) return; - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); - if (spec->gpio_write_delay) - msleep(1); - alc_write_gpio_data(codec); + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, + spec->gpio_write_delay ? 1 : 0); } -EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK"); void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask) { diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h index b2a919904c4c27..2ca15a6c2a0840 100644 --- a/sound/hda/codecs/realtek/realtek.h +++ b/sound/hda/codecs/realtek/realtek.h @@ -171,7 +171,6 @@ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask); void alc_write_gpio_data(struct hda_codec *codec); void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, bool on); -void alc_write_gpio(struct hda_codec *codec); /* common GPIO fixups */ void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask); From ef27d8ce0a3b55744b86e1866c62dc7537749180 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:17 +0200 Subject: [PATCH 637/700] ALSA: hda/alc662: Simplify the quirk for CSL Unity BF24B The previous implementation of the quirk for CSL Unity BF24B in commit de65275fc94e ("ALSA: hda/realtek: Add quirk for CSL Unity BF24B") introduced the unnecessary GPIO caching which leads to a superfluous write at each init/resume. Use the new helper to write GPIO bits directly for optimization. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-5-tiwai@suse.de --- sound/hda/codecs/realtek/alc662.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 3abe41c7315c46..2cf3664a35ff09 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -258,19 +258,11 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, static void alc662_fixup_csl_amp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gpio_mask |= 0x03; - spec->gpio_dir |= 0x03; - break; - case HDA_FIXUP_ACT_INIT: + if (action == HDA_FIXUP_ACT_INIT) { /* need to toggle GPIO to enable the amp */ - alc_update_gpio_data(codec, 0x03, true); + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0); msleep(100); - alc_update_gpio_data(codec, 0x03, false); - break; + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0); } } From 735b3739517b18983cb0347ba56d76bf1018e0c4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:18 +0200 Subject: [PATCH 638/700] ALSA: hda/analog: Fix GPIO verb orders So far we used the verb cache to restore the GPIO mask, direction and data bits at PM resume. But, due to the nature of the cache resume mechanism, the calling order isn't guaranteed, and this might lead to some inconsistency at the restored state. For assuring the GPIO verb orders, use the new GPIO helper function to explicitly set up the GPIO bits, instead of using the codec verb caches, while keeping the current data bits in ad198x_spec. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-6-tiwai@suse.de --- sound/hda/codecs/analog.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/hda/codecs/analog.c b/sound/hda/codecs/analog.c index 11b1d30b23fdf6..1ba8ae54e25ed2 100644 --- a/sound/hda/codecs/analog.c +++ b/sound/hda/codecs/analog.c @@ -38,6 +38,8 @@ struct ad198x_spec { unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ int num_smux_conns; + + unsigned int gpio_data; }; @@ -934,9 +936,9 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) if (spec->eapd_nid) ad_vmaster_eapd_hook(private_data, enabled); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, - enabled ? 0x00 : 0x02); + spec->gpio_data = enabled ? 0x00 : 0x02; + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, spec->gpio_data); } static void ad1884_fixup_hp_eapd(struct hda_codec *codec, @@ -948,12 +950,7 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, case HDA_FIXUP_ACT_PRE_PROBE: spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; spec->gen.own_eapd_ctl = 1; - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x02); + spec->gpio_data = 0x02; break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -961,6 +958,9 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, else spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; break; + case HDA_FIXUP_ACT_INIT: + snd_hda_codec_set_gpio(codec, 0x02, 0x02, spec->gpio_data, 0); + break; } } From 37e4fccc21aa0e812d77e3e3f7b8373475ee3715 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:19 +0200 Subject: [PATCH 639/700] ALSA: hda/sigmatel: Clean up with the new GPIO helper Use the new GPIO helper function to clean up the open code. Merely a code refactoring, and no behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-7-tiwai@suse.de --- sound/hda/codecs/sigmatel.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c index 4ff80a65168fdb..ee3bd21adc366a 100644 --- a/sound/hda/codecs/sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -309,15 +309,7 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, /* Configure GPIOx as CMOS */ snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - snd_hda_codec_write(codec, fg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write_sync(codec, fg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ - - msleep(1); - - snd_hda_codec_write_sync(codec, fg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ + snd_hda_codec_set_gpio(codec, gpiomask, gpiodir, gpiostate, 1); } /* hook for controlling mic-mute LED GPIO */ From d35f8e8c6fc5c92c96be38e9ca94d99233ce1ddd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:20 +0200 Subject: [PATCH 640/700] ALSA: hda/ca0132: Clean up with the new GPIO helper Use the new GPIO helper function to clean up the open code. Merely a code refactoring, and no behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-8-tiwai@suse.de --- sound/hda/codecs/ca0132.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index a0677d7da8e2d8..ad533b04ab29cf 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -3755,22 +3755,12 @@ static void ca0132_gpio_setup(struct hda_codec *codec) switch (ca0132_quirk(spec)) { case QUIRK_SBZ: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x04); + snd_hda_codec_set_gpio(codec, 0x07, 0x07, 0x04, 0); snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x06); break; case QUIRK_R3DI: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x1E); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x1F); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x0C); + snd_hda_codec_set_gpio(codec, 0x1F, 0x1E, 0x0C, 0); break; default: break; From daadb7fce1b53336acb195f34bd42d79754afa0e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:21 +0200 Subject: [PATCH 641/700] ALSA: hda/cirrus: Clean up with the new GPIO helper Use the new GPIO helper function to clean up the open code. Merely a code refactoring, and no behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-9-tiwai@suse.de --- sound/hda/codecs/cirrus/cs420x.c | 11 +++-------- sound/hda/codecs/cirrus/cs421x.c | 11 +++-------- sound/hda/codecs/cirrus/cs8409.c | 22 ++++++---------------- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c index 52eda0a338eaa7..42559edbba0553 100644 --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -264,14 +264,9 @@ static int cs_init(struct hda_codec *codec) snd_hda_gen_init(codec); - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); if (spec->vendor_nid == CS420X_VENDOR_NID) { init_input_coef(codec); diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c index c8349a2c5a3695..645b06599e5fec 100644 --- a/sound/hda/codecs/cirrus/cs421x.c +++ b/sound/hda/codecs/cirrus/cs421x.c @@ -442,14 +442,9 @@ static int cs421x_init(struct hda_codec *codec) snd_hda_gen_init(codec); - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); cs4210_spdif_automute(codec, NULL); diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c index 2d8f482e6474a0..c43ff3ef75b6e9 100644 --- a/sound/hda/codecs/cirrus/cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -1042,14 +1042,9 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec) struct cs8409_spec *spec = codec->spec; struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); for (; seq->nid; seq++) cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); @@ -1442,14 +1437,9 @@ static void dolphin_hw_init(struct hda_codec *codec) struct sub_codec *cs42l42; int i; - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } + if (spec->gpio_mask) + snd_hda_codec_set_gpio(codec, spec->gpio_mask, spec->gpio_dir, + spec->gpio_data, 0); for (; seq->nid; seq++) cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); From 9851bc2b9013674ba7dc151843f29b6255fedba3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:22 +0200 Subject: [PATCH 642/700] ALSA: hda/conexant: Clean up with the new GPIO helper Use the new GPIO helper function to clean up the open code. Merely a code refactoring, and no behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-10-tiwai@suse.de --- sound/hda/codecs/conexant.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index aa726eb323ebe2..3a9717df39b4be 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -154,14 +154,8 @@ static void cxt_init_gpio_led(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - if (mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } + if (mask) + snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0); } static void cx_fixup_headset_recog(struct hda_codec *codec) @@ -775,9 +769,7 @@ static void cxt_setup_gpio_unmute(struct hda_codec *codec, { if (gpio_mute_mask) { // set gpio data to 0. - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask); + snd_hda_codec_set_gpio(codec, gpio_mute_mask, gpio_mute_mask, 0, 0); snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0); } } From c2938a83a257ca1e3a8e74b385f543d6bd6eab8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 11:38:23 +0200 Subject: [PATCH 643/700] ALSA: hda/senarytech: Clean up with the new GPIO helper Use the new GPIO helper function to clean up the open code. Merely a code refactoring, and no behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260409093826.1317626-11-tiwai@suse.de --- sound/hda/codecs/senarytech.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c index 29b554cdd81d48..3ee8bc0ea3ab55 100644 --- a/sound/hda/codecs/senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -159,14 +159,8 @@ static void senary_init_gpio_led(struct hda_codec *codec) struct senary_spec *spec = codec->spec; unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - if (mask) { - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } + if (mask) + snd_hda_codec_set_gpio(codec, mask, mask, spec->gpio_led, 0); } static int senary_init(struct hda_codec *codec) From c71ba669b570c7b3f86ec875be222ea11dacb352 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Apr 2026 08:40:00 +0800 Subject: [PATCH 644/700] nfc: pn533: allocate rx skb before consuming bytes pn532_receive_buf() reports the number of accepted bytes to the serdev core. The current code consumes bytes into recv_skb and may already hand a complete frame to pn533_recv_frame() before allocating a fresh receive buffer. If that alloc_skb() fails, the callback returns 0 even though it has already consumed bytes, and it leaves recv_skb as NULL for the next receive callback. That breaks the receive_buf() accounting contract and can also lead to a NULL dereference on the next skb_put_u8(). Allocate the receive skb lazily before consuming the next byte instead. If allocation fails, return the number of bytes already accepted. Fixes: c656aa4c27b1 ("nfc: pn533: add UART phy driver") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260405094003.3-pn533-v2-pengpeng@iscas.ac.cn Signed-off-by: Paolo Abeni --- drivers/nfc/pn533/uart.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index 1b82b7b2a5fae8..e0d67cd2ac9b8f 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -211,6 +211,13 @@ static size_t pn532_receive_buf(struct serdev_device *serdev, timer_delete(&dev->cmd_timeout); for (i = 0; i < count; i++) { + if (!dev->recv_skb) { + dev->recv_skb = alloc_skb(PN532_UART_SKB_BUFF_LEN, + GFP_KERNEL); + if (!dev->recv_skb) + return i; + } + if (unlikely(!skb_tailroom(dev->recv_skb))) skb_trim(dev->recv_skb, 0); @@ -219,9 +226,7 @@ static size_t pn532_receive_buf(struct serdev_device *serdev, continue; pn533_recv_frame(dev->priv, dev->recv_skb, 0); - dev->recv_skb = alloc_skb(PN532_UART_SKB_BUFF_LEN, GFP_KERNEL); - if (!dev->recv_skb) - return 0; + dev->recv_skb = NULL; } return i; From 7e0548525abd2bff9694e016b6a469ccd2d5a053 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 8 Apr 2026 15:40:57 +0100 Subject: [PATCH 645/700] iommu: Ensure .iotlb_sync is called correctly Many drivers have no reason to use the iotlb_gather mechanism, but do still depend on .iotlb_sync being called to properly complete an unmap. Since the core code is now relying on the gather to detect when there is legitimately something to sync, it should also take care of encoding a successful unmap when the driver does not touch the gather itself. Fixes: 90c5def10bea ("iommu: Do not call drivers for empty gathers") Reported-by: Jon Hunter Closes: https://lore.kernel.org/r/8800a38b-8515-4bbe-af15-0dae81274bf7@nvidia.com Signed-off-by: Robin Murphy Tested-by: Jon Hunter Reviewed-by: Jason Gunthorpe Tested-by: Russell King (Oracle) Signed-off-by: Will Deacon --- drivers/iommu/iommu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 50718ab810a413..ee83850c70605e 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2717,6 +2717,12 @@ static size_t __iommu_unmap(struct iommu_domain *domain, pr_debug("unmapped: iova 0x%lx size 0x%zx\n", iova, unmapped_page); + /* + * If the driver itself isn't using the gather, make sure + * it looks non-empty so iotlb_sync will still be called. + */ + if (iotlb_gather->start >= iotlb_gather->end) + iommu_iotlb_gather_add_range(iotlb_gather, iova, size); iova += unmapped_page; unmapped += unmapped_page; From e3b2cf6e5dba416a03152f299d99982dfe1e861d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 1 Apr 2026 12:15:58 +0200 Subject: [PATCH 646/700] kernfs: pass struct ns_common instead of const void * for namespace tags kernfs has historically used const void * to pass around namespace tags used for directory-level namespace filtering. The only current user of this is sysfs network namespace tagging where struct net pointers are cast to void *. Replace all const void * namespace parameters with const struct ns_common * throughout the kernfs, sysfs, and kobject namespace layers. This includes the kobj_ns_type_operations callbacks, kobject_namespace(), and all sysfs/kernfs APIs that accept or return namespace tags. Passing struct ns_common is needed because various codepaths require access to the underlying namespace. A struct ns_common can always be converted back to the concrete namespace type (e.g., struct net) via container_of() or to_ns_common() in the reverse direction. This is a preparatory change for switching to ns_id-based directory iteration to prevent a KASLR pointer leak through the current use of raw namespace pointers as hash seeds and comparison keys. Signed-off-by: Christian Brauner --- drivers/base/class.c | 4 +-- drivers/base/core.c | 7 ++-- drivers/infiniband/core/device.c | 5 +-- drivers/infiniband/ulp/srp/ib_srp.c | 7 ++-- drivers/net/bonding/bond_sysfs.c | 4 +-- drivers/net/ipvlan/ipvtap.c | 5 +-- drivers/net/macvtap.c | 5 +-- fs/kernfs/dir.c | 30 +++++++++-------- fs/kernfs/file.c | 2 +- fs/kernfs/kernfs-internal.h | 2 +- fs/kernfs/mount.c | 2 +- fs/nfs/sysfs.c | 16 +++++---- fs/sysfs/dir.c | 6 ++-- fs/sysfs/file.c | 8 ++--- fs/sysfs/mount.c | 10 +++--- fs/sysfs/symlink.c | 7 ++-- fs/sysfs/sysfs.h | 4 +-- include/linux/device/class.h | 6 ++-- include/linux/kernfs.h | 40 ++++++++++++++--------- include/linux/kobject.h | 4 +-- include/linux/kobject_ns.h | 13 ++++---- include/linux/netdevice.h | 4 +-- include/linux/sysfs.h | 24 +++++++------- include/net/net_namespace.h | 8 ++--- lib/kobject.c | 8 ++--- lib/kobject_uevent.c | 13 +++++--- net/core/net-sysfs.c | 50 ++++++++++++++--------------- net/core/net_namespace.c | 8 ++--- net/sunrpc/sysfs.c | 17 ++++++---- net/wireless/sysfs.c | 4 +-- 30 files changed, 175 insertions(+), 148 deletions(-) diff --git a/drivers/base/class.c b/drivers/base/class.c index 827fc7adacc7e2..ffab0a9c8ccbae 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -127,7 +127,7 @@ static const struct kobj_type class_ktype = { }; int class_create_file_ns(const struct class *cls, const struct class_attribute *attr, - const void *ns) + const struct ns_common *ns) { struct subsys_private *sp = class_to_subsys(cls); int error; @@ -143,7 +143,7 @@ int class_create_file_ns(const struct class *cls, const struct class_attribute * EXPORT_SYMBOL_GPL(class_create_file_ns); void class_remove_file_ns(const struct class *cls, const struct class_attribute *attr, - const void *ns) + const struct ns_common *ns) { struct subsys_private *sp = class_to_subsys(cls); diff --git a/drivers/base/core.c b/drivers/base/core.c index 09b98f02f559d0..0613de0fbe44dc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2570,15 +2570,14 @@ static void device_release(struct kobject *kobj) kfree(p); } -static const void *device_namespace(const struct kobject *kobj) +static const struct ns_common *device_namespace(const struct kobject *kobj) { const struct device *dev = kobj_to_dev(kobj); - const void *ns = NULL; if (dev->class && dev->class->namespace) - ns = dev->class->namespace(dev); + return dev->class->namespace(dev); - return ns; + return NULL; } static void device_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 558b73940d6681..7945614be36d47 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -509,12 +509,13 @@ static int ib_device_uevent(const struct device *device, return 0; } -static const void *net_namespace(const struct device *d) +static const struct ns_common *net_namespace(const struct device *d) { const struct ib_core_device *coredev = container_of(d, struct ib_core_device, dev); + struct net *net = read_pnet(&coredev->rdma_net); - return read_pnet(&coredev->rdma_net); + return net ? to_ns_common(net) : NULL; } static struct class ib_class = { diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 30339dcabb4d13..b58868e1cf11c2 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -1048,7 +1049,7 @@ static void srp_remove_target(struct srp_target_port *target) scsi_remove_host(target->scsi_host); srp_stop_rport_timers(target->rport); srp_disconnect_target(target); - kobj_ns_drop(KOBJ_NS_TYPE_NET, target->net); + kobj_ns_drop(KOBJ_NS_TYPE_NET, to_ns_common(target->net)); for (i = 0; i < target->ch_count; i++) { ch = &target->ch[i]; srp_free_ch_ib(target, ch); @@ -3713,7 +3714,7 @@ static ssize_t add_target_store(struct device *dev, target = host_to_target(target_host); - target->net = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); + target->net = to_net_ns(kobj_ns_grab_current(KOBJ_NS_TYPE_NET)); target->io_class = SRP_REV16A_IB_IO_CLASS; target->scsi_host = target_host; target->srp_host = host; @@ -3905,7 +3906,7 @@ static ssize_t add_target_store(struct device *dev, * earlier in this function. */ if (target->state != SRP_TARGET_REMOVED) - kobj_ns_drop(KOBJ_NS_TYPE_NET, target->net); + kobj_ns_drop(KOBJ_NS_TYPE_NET, to_ns_common(target->net)); scsi_host_put(target->scsi_host); } diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 9a75ad3181ab5c..eaba44c76a5ec6 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -808,7 +808,7 @@ int __net_init bond_create_sysfs(struct bond_net *bn) sysfs_attr_init(&bn->class_attr_bonding_masters.attr); ret = netdev_class_create_file_ns(&bn->class_attr_bonding_masters, - bn->net); + to_ns_common(bn->net)); /* Permit multiple loads of the module by ignoring failures to * create the bonding_masters sysfs file. Bonding devices * created by second or subsequent loads of the module will @@ -835,7 +835,7 @@ int __net_init bond_create_sysfs(struct bond_net *bn) /* Remove /sys/class/net/bonding_masters. */ void __net_exit bond_destroy_sysfs(struct bond_net *bn) { - netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, bn->net); + netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, to_ns_common(bn->net)); } /* Initialize sysfs for each bond. This sets up and registers diff --git a/drivers/net/ipvlan/ipvtap.c b/drivers/net/ipvlan/ipvtap.c index edd13916831a7c..2d6bbddd1edd51 100644 --- a/drivers/net/ipvlan/ipvtap.c +++ b/drivers/net/ipvlan/ipvtap.c @@ -30,10 +30,11 @@ static dev_t ipvtap_major; static struct cdev ipvtap_cdev; -static const void *ipvtap_net_namespace(const struct device *d) +static const struct ns_common *ipvtap_net_namespace(const struct device *d) { const struct net_device *dev = to_net_dev(d->parent); - return dev_net(dev); + + return to_ns_common(dev_net(dev)); } static struct class ipvtap_class = { diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index b391a0f740a3f3..cc975dfb738051 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -35,10 +35,11 @@ struct macvtap_dev { */ static dev_t macvtap_major; -static const void *macvtap_net_namespace(const struct device *d) +static const struct ns_common *macvtap_net_namespace(const struct device *d) { const struct net_device *dev = to_net_dev(d->parent); - return dev_net(dev); + + return to_ns_common(dev_net(dev)); } static struct class macvtap_class = { diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 8d40c4b1db9fff..be262145ae0847 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -313,7 +313,8 @@ struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) * * Return: 31-bit hash of ns + name (so it fits in an off_t) */ -static unsigned int kernfs_name_hash(const char *name, const void *ns) +static unsigned int kernfs_name_hash(const char *name, + const struct ns_common *ns) { unsigned long hash = init_name_hash(ns); unsigned int len = strlen(name); @@ -330,7 +331,7 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns) } static int kernfs_name_compare(unsigned int hash, const char *name, - const void *ns, const struct kernfs_node *kn) + const struct ns_common *ns, const struct kernfs_node *kn) { if (hash < kn->hash) return -1; @@ -856,7 +857,7 @@ int kernfs_add_one(struct kernfs_node *kn) */ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, const unsigned char *name, - const void *ns) + const struct ns_common *ns) { struct rb_node *node = parent->dir.children.rb_node; bool has_ns = kernfs_ns_enabled(parent); @@ -889,7 +890,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent, const unsigned char *path, - const void *ns) + const struct ns_common *ns) { ssize_t len; char *p, *name; @@ -930,7 +931,8 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent, * Return: pointer to the found kernfs_node on success, %NULL on failure. */ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, - const char *name, const void *ns) + const char *name, + const struct ns_common *ns) { struct kernfs_node *kn; struct kernfs_root *root = kernfs_root(parent); @@ -956,7 +958,8 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); * Return: pointer to the found kernfs_node on success, %NULL on failure. */ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent, - const char *path, const void *ns) + const char *path, + const struct ns_common *ns) { struct kernfs_node *kn; struct kernfs_root *root = kernfs_root(parent); @@ -1079,7 +1082,8 @@ struct kernfs_node *kernfs_root_to_node(struct kernfs_root *root) struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, - void *priv, const void *ns) + void *priv, + const struct ns_common *ns) { struct kernfs_node *kn; int rc; @@ -1221,7 +1225,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, struct kernfs_node *kn; struct kernfs_root *root; struct inode *inode = NULL; - const void *ns = NULL; + const struct ns_common *ns = NULL; root = kernfs_root(parent); down_read(&root->kernfs_rwsem); @@ -1702,7 +1706,7 @@ bool kernfs_remove_self(struct kernfs_node *kn) * Return: %0 on success, -ENOENT if such entry doesn't exist. */ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, - const void *ns) + const struct ns_common *ns) { struct kernfs_node *kn; struct kernfs_root *root; @@ -1741,7 +1745,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, * Return: %0 on success, -errno on failure. */ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, - const char *new_name, const void *new_ns) + const char *new_name, const struct ns_common *new_ns) { struct kernfs_node *old_parent; struct kernfs_root *root; @@ -1832,7 +1836,7 @@ static int kernfs_dir_fop_release(struct inode *inode, struct file *filp) return 0; } -static struct kernfs_node *kernfs_dir_pos(const void *ns, +static struct kernfs_node *kernfs_dir_pos(const struct ns_common *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { @@ -1867,7 +1871,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, return pos; } -static struct kernfs_node *kernfs_dir_next_pos(const void *ns, +static struct kernfs_node *kernfs_dir_next_pos(const struct ns_common *ns, struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) { pos = kernfs_dir_pos(ns, parent, ino, pos); @@ -1889,7 +1893,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) struct kernfs_node *parent = kernfs_dentry_node(dentry); struct kernfs_node *pos = file->private_data; struct kernfs_root *root; - const void *ns = NULL; + const struct ns_common *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index e32406d62c0d36..1163aa76973849 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -1045,7 +1045,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, umode_t mode, kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops, - void *priv, const void *ns, + void *priv, const struct ns_common *ns, struct lock_class_key *key) { struct kernfs_node *kn; diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 6061b6f70d2a3c..b1fd9622a5e34d 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -97,7 +97,7 @@ struct kernfs_super_info { * instance. If multiple tags become necessary, make the following * an array and compare kernfs_node tag against every entry. */ - const void *ns; + const struct ns_common *ns; /* anchored at kernfs_root->supers, protected by kernfs_rwsem */ struct list_head node; diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 048f00b73b7173..6e3217b6e4811a 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -345,7 +345,7 @@ static int kernfs_set_super(struct super_block *sb, struct fs_context *fc) * * Return: the namespace tag associated with kernfs super_block @sb. */ -const void *kernfs_super_ns(struct super_block *sb) +const struct ns_common *kernfs_super_ns(struct super_block *sb) { struct kernfs_super_info *info = kernfs_info(sb); diff --git a/fs/nfs/sysfs.c b/fs/nfs/sysfs.c index 7d8921f524a694..1da4f707f9efee 100644 --- a/fs/nfs/sysfs.c +++ b/fs/nfs/sysfs.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -127,9 +128,10 @@ static void nfs_netns_client_release(struct kobject *kobj) kfree(rcu_dereference_raw(c->identifier)); } -static const void *nfs_netns_client_namespace(const struct kobject *kobj) +static const struct ns_common *nfs_netns_client_namespace(const struct kobject *kobj) { - return container_of(kobj, struct nfs_netns_client, kobject)->net; + return to_ns_common(container_of(kobj, struct nfs_netns_client, + kobject)->net); } static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, @@ -156,9 +158,10 @@ static void nfs_netns_object_release(struct kobject *kobj) kfree(c); } -static const void *nfs_netns_namespace(const struct kobject *kobj) +static const struct ns_common *nfs_netns_namespace(const struct kobject *kobj) { - return container_of(kobj, struct nfs_netns_client, nfs_net_kobj)->net; + return to_ns_common(container_of(kobj, struct nfs_netns_client, + nfs_net_kobj)->net); } static struct kobj_type nfs_netns_object_type = { @@ -350,9 +353,10 @@ static void nfs_sysfs_sb_release(struct kobject *kobj) /* no-op: why? see lib/kobject.c kobject_cleanup() */ } -static const void *nfs_netns_server_namespace(const struct kobject *kobj) +static const struct ns_common *nfs_netns_server_namespace(const struct kobject *kobj) { - return container_of(kobj, struct nfs_server, kobj)->nfs_client->cl_net; + return to_ns_common(container_of(kobj, struct nfs_server, + kobj)->nfs_client->cl_net); } static struct kobj_type nfs_sb_ktype = { diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 94e12efd92f216..ffdcd4153c5848 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -37,7 +37,7 @@ void sysfs_warn_dup(struct kernfs_node *parent, const char *name) * @kobj: object we're creating directory for * @ns: the namespace tag to use */ -int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) +int sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns) { struct kernfs_node *parent, *kn; kuid_t uid; @@ -103,7 +103,7 @@ void sysfs_remove_dir(struct kobject *kobj) } int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, - const void *new_ns) + const struct ns_common *new_ns) { struct kernfs_node *parent; int ret; @@ -115,7 +115,7 @@ int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, } int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, - const void *new_ns) + const struct ns_common *new_ns) { struct kernfs_node *kn = kobj->sd; struct kernfs_node *new_parent; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a8176c875f55e7..5709cede1d7568 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -272,7 +272,7 @@ static const struct kernfs_ops sysfs_bin_kfops_mmap = { int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, umode_t mode, kuid_t uid, - kgid_t gid, const void *ns) + kgid_t gid, const struct ns_common *ns) { struct kobject *kobj = parent->priv; const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; @@ -322,7 +322,7 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, const struct bin_attribute *battr, umode_t mode, size_t size, - kuid_t uid, kgid_t gid, const void *ns) + kuid_t uid, kgid_t gid, const struct ns_common *ns) { const struct attribute *attr = &battr->attr; struct lock_class_key *key = NULL; @@ -362,7 +362,7 @@ int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, * @ns: namespace the new file should belong to */ int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns) + const struct ns_common *ns) { kuid_t uid; kgid_t gid; @@ -505,7 +505,7 @@ EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection); * Hash the attribute name and namespace tag and kill the victim. */ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns) + const struct ns_common *ns) { struct kernfs_node *parent = kobj->sd; diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index e65c60158a04ac..b199e8ff79b1f9 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -55,7 +55,7 @@ static const struct fs_context_operations sysfs_fs_context_ops = { static int sysfs_init_fs_context(struct fs_context *fc) { struct kernfs_fs_context *kfc; - struct net *netns; + struct ns_common *ns; if (!(fc->sb_flags & SB_KERNMOUNT)) { if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) @@ -66,12 +66,14 @@ static int sysfs_init_fs_context(struct fs_context *fc) if (!kfc) return -ENOMEM; - kfc->ns_tag = netns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); + kfc->ns_tag = ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); kfc->root = sysfs_root; kfc->magic = SYSFS_MAGIC; fc->fs_private = kfc; fc->ops = &sysfs_fs_context_ops; - if (netns) { + if (ns) { + struct net *netns = to_net_ns(ns); + put_user_ns(fc->user_ns); fc->user_ns = get_user_ns(netns->user_ns); } @@ -81,7 +83,7 @@ static int sysfs_init_fs_context(struct fs_context *fc) static void sysfs_kill_sb(struct super_block *sb) { - void *ns = (void *)kernfs_super_ns(sb); + struct ns_common *ns = (struct ns_common *)kernfs_super_ns(sb); kernfs_kill_sb(sb); kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 5603530a1a520d..5f9c05fb13940b 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -121,7 +121,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_link_nowarn); void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, const char *name) { - const void *ns = NULL; + const struct ns_common *ns = NULL; /* * We don't own @target and it may be removed at any time. @@ -164,10 +164,11 @@ EXPORT_SYMBOL_GPL(sysfs_remove_link); * A helper function for the common rename symlink idiom. */ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, - const char *old, const char *new, const void *new_ns) + const char *old, const char *new, + const struct ns_common *new_ns) { struct kernfs_node *parent, *kn = NULL; - const void *old_ns = NULL; + const struct ns_common *old_ns = NULL; int result; if (!kobj) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 8e012f25e1c06e..f4583dcafcd1ef 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -29,10 +29,10 @@ void sysfs_warn_dup(struct kernfs_node *parent, const char *name); */ int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, umode_t amode, kuid_t uid, - kgid_t gid, const void *ns); + kgid_t gid, const struct ns_common *ns); int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, const struct bin_attribute *battr, umode_t mode, size_t size, - kuid_t uid, kgid_t gid, const void *ns); + kuid_t uid, kgid_t gid, const struct ns_common *ns); /* * symlink.c diff --git a/include/linux/device/class.h b/include/linux/device/class.h index 65880e60c72083..021da0d61796a5 100644 --- a/include/linux/device/class.h +++ b/include/linux/device/class.h @@ -62,7 +62,7 @@ struct class { int (*shutdown_pre)(struct device *dev); const struct kobj_ns_type_operations *ns_type; - const void *(*namespace)(const struct device *dev); + const struct ns_common *(*namespace)(const struct device *dev); void (*get_ownership)(const struct device *dev, kuid_t *uid, kgid_t *gid); @@ -180,9 +180,9 @@ struct class_attribute { struct class_attribute class_attr_##_name = __ATTR_WO(_name) int __must_check class_create_file_ns(const struct class *class, const struct class_attribute *attr, - const void *ns); + const struct ns_common *ns); void class_remove_file_ns(const struct class *class, const struct class_attribute *attr, - const void *ns); + const struct ns_common *ns); static inline int __must_check class_create_file(const struct class *class, const struct class_attribute *attr) diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index b5a5f32fdfd1af..4f0ab88a1b3166 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -23,6 +23,7 @@ struct file; struct dentry; struct iattr; +struct ns_common; struct seq_file; struct vm_area_struct; struct vm_operations_struct; @@ -209,7 +210,7 @@ struct kernfs_node { struct rb_node rb; - const void *ns; /* namespace tag */ + const struct ns_common *ns; /* namespace tag */ unsigned int hash; /* ns + name hash */ unsigned short flags; umode_t mode; @@ -331,7 +332,7 @@ struct kernfs_ops { */ struct kernfs_fs_context { struct kernfs_root *root; /* Root of the hierarchy being mounted */ - void *ns_tag; /* Namespace tag of the mount (or NULL) */ + struct ns_common *ns_tag; /* Namespace tag of the mount (or NULL) */ unsigned long magic; /* File system specific magic number */ /* The following are set/used by kernfs_mount() */ @@ -406,9 +407,11 @@ void pr_cont_kernfs_name(struct kernfs_node *kn); void pr_cont_kernfs_path(struct kernfs_node *kn); struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, - const char *name, const void *ns); + const char *name, + const struct ns_common *ns); struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent, - const char *path, const void *ns); + const char *path, + const struct ns_common *ns); void kernfs_get(struct kernfs_node *kn); void kernfs_put(struct kernfs_node *kn); @@ -426,7 +429,8 @@ unsigned int kernfs_root_flags(struct kernfs_node *kn); struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, - void *priv, const void *ns); + void *priv, + const struct ns_common *ns); struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent, const char *name); struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, @@ -434,7 +438,8 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops, - void *priv, const void *ns, + void *priv, + const struct ns_common *ns, struct lock_class_key *key); struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, const char *name, @@ -446,9 +451,9 @@ void kernfs_break_active_protection(struct kernfs_node *kn); void kernfs_unbreak_active_protection(struct kernfs_node *kn); bool kernfs_remove_self(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, - const void *ns); + const struct ns_common *ns); int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, - const char *new_name, const void *new_ns); + const char *new_name, const struct ns_common *new_ns); int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr); __poll_t kernfs_generic_poll(struct kernfs_open_file *of, struct poll_table_struct *pt); @@ -459,7 +464,7 @@ int kernfs_xattr_get(struct kernfs_node *kn, const char *name, int kernfs_xattr_set(struct kernfs_node *kn, const char *name, const void *value, size_t size, int flags); -const void *kernfs_super_ns(struct super_block *sb); +const struct ns_common *kernfs_super_ns(struct super_block *sb); int kernfs_get_tree(struct fs_context *fc); void kernfs_free_fs_context(struct fs_context *fc); void kernfs_kill_sb(struct super_block *sb); @@ -494,11 +499,11 @@ static inline struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) static inline struct kernfs_node * kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, - const void *ns) + const struct ns_common *ns) { return NULL; } static inline struct kernfs_node * kernfs_walk_and_get_ns(struct kernfs_node *parent, const char *path, - const void *ns) + const struct ns_common *ns) { return NULL; } static inline void kernfs_get(struct kernfs_node *kn) { } @@ -526,14 +531,15 @@ static inline unsigned int kernfs_root_flags(struct kernfs_node *kn) static inline struct kernfs_node * kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, - void *priv, const void *ns) + void *priv, const struct ns_common *ns) { return ERR_PTR(-ENOSYS); } static inline struct kernfs_node * __kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, kuid_t uid, kgid_t gid, loff_t size, const struct kernfs_ops *ops, - void *priv, const void *ns, struct lock_class_key *key) + void *priv, const struct ns_common *ns, + struct lock_class_key *key) { return ERR_PTR(-ENOSYS); } static inline struct kernfs_node * @@ -549,12 +555,14 @@ static inline bool kernfs_remove_self(struct kernfs_node *kn) { return false; } static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, - const char *name, const void *ns) + const char *name, + const struct ns_common *ns) { return -ENOSYS; } static inline int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, - const char *new_name, const void *new_ns) + const char *new_name, + const struct ns_common *new_ns) { return -ENOSYS; } static inline int kernfs_setattr(struct kernfs_node *kn, @@ -575,7 +583,7 @@ static inline int kernfs_xattr_set(struct kernfs_node *kn, const char *name, const void *value, size_t size, int flags) { return -ENOSYS; } -static inline const void *kernfs_super_ns(struct super_block *sb) +static inline const struct ns_common *kernfs_super_ns(struct super_block *sb) { return NULL; } static inline int kernfs_get_tree(struct fs_context *fc) diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c8219505a79f98..bcb5d4e320015a 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -109,7 +109,7 @@ struct kobject *kobject_get(struct kobject *kobj); struct kobject * __must_check kobject_get_unless_zero(struct kobject *kobj); void kobject_put(struct kobject *kobj); -const void *kobject_namespace(const struct kobject *kobj); +const struct ns_common *kobject_namespace(const struct kobject *kobj); void kobject_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid); char *kobject_get_path(const struct kobject *kobj, gfp_t flag); @@ -118,7 +118,7 @@ struct kobj_type { const struct sysfs_ops *sysfs_ops; const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(const struct kobject *kobj); - const void *(*namespace)(const struct kobject *kobj); + const struct ns_common *(*namespace)(const struct kobject *kobj); void (*get_ownership)(const struct kobject *kobj, kuid_t *uid, kgid_t *gid); }; diff --git a/include/linux/kobject_ns.h b/include/linux/kobject_ns.h index 150fe2ae1b6b25..4f0990e09b93e1 100644 --- a/include/linux/kobject_ns.h +++ b/include/linux/kobject_ns.h @@ -16,6 +16,7 @@ #ifndef _LINUX_KOBJECT_NS_H #define _LINUX_KOBJECT_NS_H +struct ns_common; struct sock; struct kobject; @@ -39,10 +40,10 @@ enum kobj_ns_type { struct kobj_ns_type_operations { enum kobj_ns_type type; bool (*current_may_mount)(void); - void *(*grab_current_ns)(void); - const void *(*netlink_ns)(struct sock *sk); - const void *(*initial_ns)(void); - void (*drop_ns)(void *); + struct ns_common *(*grab_current_ns)(void); + const struct ns_common *(*netlink_ns)(struct sock *sk); + const struct ns_common *(*initial_ns)(void); + void (*drop_ns)(struct ns_common *); }; int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); @@ -51,7 +52,7 @@ const struct kobj_ns_type_operations *kobj_child_ns_ops(const struct kobject *pa const struct kobj_ns_type_operations *kobj_ns_ops(const struct kobject *kobj); bool kobj_ns_current_may_mount(enum kobj_ns_type type); -void *kobj_ns_grab_current(enum kobj_ns_type type); -void kobj_ns_drop(enum kobj_ns_type type, void *ns); +struct ns_common *kobj_ns_grab_current(enum kobj_ns_type type); +void kobj_ns_drop(enum kobj_ns_type type, struct ns_common *ns); #endif /* _LINUX_KOBJECT_NS_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7ca01eb3f7d2b2..85c20bdd36fbdd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -5339,9 +5339,9 @@ static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_devi } int netdev_class_create_file_ns(const struct class_attribute *class_attr, - const void *ns); + const struct ns_common *ns); void netdev_class_remove_file_ns(const struct class_attribute *class_attr, - const void *ns); + const struct ns_common *ns); extern const struct kobj_ns_type_operations net_ns_type_operations; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 99b775f3ff469f..468259fb6049ee 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -396,13 +396,13 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS -int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); +int __must_check sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, - const void *new_ns); + const struct ns_common *new_ns); int __must_check sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, - const void *new_ns); + const struct ns_common *new_ns); int __must_check sysfs_create_mount_point(struct kobject *parent_kobj, const char *name); void sysfs_remove_mount_point(struct kobject *parent_kobj, @@ -410,7 +410,7 @@ void sysfs_remove_mount_point(struct kobject *parent_kobj, int __must_check sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns); + const struct ns_common *ns); int __must_check sysfs_create_files(struct kobject *kobj, const struct attribute * const *attr); int __must_check sysfs_chmod_file(struct kobject *kobj, @@ -419,7 +419,7 @@ struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj, const struct attribute *attr); void sysfs_unbreak_active_protection(struct kernfs_node *kn); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns); + const struct ns_common *ns); bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *attr); @@ -437,7 +437,7 @@ void sysfs_remove_link(struct kobject *kobj, const char *name); int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target, const char *old_name, const char *new_name, - const void *new_ns); + const struct ns_common *new_ns); void sysfs_delete_link(struct kobject *dir, struct kobject *targ, const char *name); @@ -502,7 +502,7 @@ ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj, #else /* CONFIG_SYSFS */ -static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) +static inline int sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns) { return 0; } @@ -512,14 +512,14 @@ static inline void sysfs_remove_dir(struct kobject *kobj) } static inline int sysfs_rename_dir_ns(struct kobject *kobj, - const char *new_name, const void *new_ns) + const char *new_name, const struct ns_common *new_ns) { return 0; } static inline int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, - const void *new_ns) + const struct ns_common *new_ns) { return 0; } @@ -537,7 +537,7 @@ static inline void sysfs_remove_mount_point(struct kobject *parent_kobj, static inline int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns) + const struct ns_common *ns) { return 0; } @@ -567,7 +567,7 @@ static inline void sysfs_unbreak_active_protection(struct kernfs_node *kn) static inline void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, - const void *ns) + const struct ns_common *ns) { } @@ -612,7 +612,7 @@ static inline void sysfs_remove_link(struct kobject *kobj, const char *name) static inline int sysfs_rename_link_ns(struct kobject *k, struct kobject *t, const char *old_name, - const char *new_name, const void *ns) + const char *new_name, const struct ns_common *ns) { return 0; } diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index d7bec49ee9ea91..80de5e98a66d6c 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -264,14 +264,14 @@ void ipx_unregister_sysctl(void); #define ipx_unregister_sysctl() #endif -#ifdef CONFIG_NET_NS -void __put_net(struct net *net); - static inline struct net *to_net_ns(struct ns_common *ns) { return container_of(ns, struct net, ns); } +#ifdef CONFIG_NET_NS +void __put_net(struct net *net); + /* Try using get_net_track() instead */ static inline struct net *get_net(struct net *net) { @@ -309,7 +309,7 @@ static inline int check_net(const struct net *net) return ns_ref_read(net) != 0; } -void net_drop_ns(void *); +void net_drop_ns(struct ns_common *); void net_passive_dec(struct net *net); #else diff --git a/lib/kobject.c b/lib/kobject.c index cfdb2c3f20a2ef..9c9ff0f5175fb4 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -27,7 +27,7 @@ * and thus @kobj should have a namespace tag associated with it. Returns * %NULL otherwise. */ -const void *kobject_namespace(const struct kobject *kobj) +const struct ns_common *kobject_namespace(const struct kobject *kobj) { const struct kobj_ns_type_operations *ns_ops = kobj_ns_ops(kobj); @@ -1083,9 +1083,9 @@ bool kobj_ns_current_may_mount(enum kobj_ns_type type) return may_mount; } -void *kobj_ns_grab_current(enum kobj_ns_type type) +struct ns_common *kobj_ns_grab_current(enum kobj_ns_type type) { - void *ns = NULL; + struct ns_common *ns = NULL; spin_lock(&kobj_ns_type_lock); if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type]) @@ -1096,7 +1096,7 @@ void *kobj_ns_grab_current(enum kobj_ns_type type) } EXPORT_SYMBOL_GPL(kobj_ns_grab_current); -void kobj_ns_drop(enum kobj_ns_type type, void *ns) +void kobj_ns_drop(enum kobj_ns_type type, struct ns_common *ns) { spin_lock(&kobj_ns_type_lock); if (kobj_ns_type_is_valid(type) && diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 871941c9830cf5..ddbc4d7482d243 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -238,7 +238,7 @@ static int kobj_usermode_filter(struct kobject *kobj) ops = kobj_ns_ops(kobj); if (ops) { - const void *init_ns, *ns; + const struct ns_common *init_ns, *ns; ns = kobj->ktype->namespace(kobj); init_ns = ops->initial_ns(); @@ -388,7 +388,7 @@ static int kobject_uevent_net_broadcast(struct kobject *kobj, #ifdef CONFIG_NET const struct kobj_ns_type_operations *ops; - const struct net *net = NULL; + const struct ns_common *ns = NULL; ops = kobj_ns_ops(kobj); if (!ops && kobj->kset) { @@ -404,14 +404,17 @@ static int kobject_uevent_net_broadcast(struct kobject *kobj, */ if (ops && ops->netlink_ns && kobj->ktype->namespace) if (ops->type == KOBJ_NS_TYPE_NET) - net = kobj->ktype->namespace(kobj); + ns = kobj->ktype->namespace(kobj); - if (!net) + if (!ns) ret = uevent_net_broadcast_untagged(env, action_string, devpath); - else + else { + const struct net *net = container_of(ns, struct net, ns); + ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env, action_string, devpath); + } #endif return ret; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 07624b682b08b2..b9740a397f55b8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1181,24 +1181,24 @@ static void rx_queue_release(struct kobject *kobj) netdev_put(queue->dev, &queue->dev_tracker); } -static const void *rx_queue_namespace(const struct kobject *kobj) +static const struct ns_common *rx_queue_namespace(const struct kobject *kobj) { struct netdev_rx_queue *queue = to_rx_queue(kobj); struct device *dev = &queue->dev->dev; - const void *ns = NULL; if (dev->class && dev->class->namespace) - ns = dev->class->namespace(dev); + return dev->class->namespace(dev); - return ns; + return NULL; } static void rx_queue_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid) { - const struct net *net = rx_queue_namespace(kobj); + const struct ns_common *ns = rx_queue_namespace(kobj); - net_ns_get_ownership(net, uid, gid); + net_ns_get_ownership(ns ? container_of(ns, struct net, ns) : NULL, + uid, gid); } static const struct kobj_type rx_queue_ktype = { @@ -1931,24 +1931,24 @@ static void netdev_queue_release(struct kobject *kobj) netdev_put(queue->dev, &queue->dev_tracker); } -static const void *netdev_queue_namespace(const struct kobject *kobj) +static const struct ns_common *netdev_queue_namespace(const struct kobject *kobj) { struct netdev_queue *queue = to_netdev_queue(kobj); struct device *dev = &queue->dev->dev; - const void *ns = NULL; if (dev->class && dev->class->namespace) - ns = dev->class->namespace(dev); + return dev->class->namespace(dev); - return ns; + return NULL; } static void netdev_queue_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid) { - const struct net *net = netdev_queue_namespace(kobj); + const struct ns_common *ns = netdev_queue_namespace(kobj); - net_ns_get_ownership(net, uid, gid); + net_ns_get_ownership(ns ? container_of(ns, struct net, ns) : NULL, + uid, gid); } static const struct kobj_type netdev_queue_ktype = { @@ -2185,24 +2185,24 @@ static bool net_current_may_mount(void) return ns_capable(net->user_ns, CAP_SYS_ADMIN); } -static void *net_grab_current_ns(void) +static struct ns_common *net_grab_current_ns(void) { - struct net *ns = current->nsproxy->net_ns; + struct net *net = current->nsproxy->net_ns; #ifdef CONFIG_NET_NS - if (ns) - refcount_inc(&ns->passive); + if (net) + refcount_inc(&net->passive); #endif - return ns; + return net ? to_ns_common(net) : NULL; } -static const void *net_initial_ns(void) +static const struct ns_common *net_initial_ns(void) { - return &init_net; + return to_ns_common(&init_net); } -static const void *net_netlink_ns(struct sock *sk) +static const struct ns_common *net_netlink_ns(struct sock *sk) { - return sock_net(sk); + return to_ns_common(sock_net(sk)); } const struct kobj_ns_type_operations net_ns_type_operations = { @@ -2252,11 +2252,11 @@ static void netdev_release(struct device *d) kvfree(dev); } -static const void *net_namespace(const struct device *d) +static const struct ns_common *net_namespace(const struct device *d) { const struct net_device *dev = to_net_dev(d); - return dev_net(dev); + return to_ns_common(dev_net(dev)); } static void net_get_ownership(const struct device *d, kuid_t *uid, kgid_t *gid) @@ -2402,14 +2402,14 @@ int netdev_change_owner(struct net_device *ndev, const struct net *net_old, } int netdev_class_create_file_ns(const struct class_attribute *class_attr, - const void *ns) + const struct ns_common *ns) { return class_create_file_ns(&net_class, class_attr, ns); } EXPORT_SYMBOL(netdev_class_create_file_ns); void netdev_class_remove_file_ns(const struct class_attribute *class_attr, - const void *ns) + const struct ns_common *ns) { class_remove_file_ns(&net_class, class_attr, ns); } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 1057d16d5dd28a..24aa10a1d0eaa8 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -540,12 +540,10 @@ void net_passive_dec(struct net *net) } } -void net_drop_ns(void *p) +void net_drop_ns(struct ns_common *ns) { - struct net *net = (struct net *)p; - - if (net) - net_passive_dec(net); + if (ns) + net_passive_dec(to_net_ns(ns)); } struct net *copy_net_ns(u64 flags, diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index af8fac9cedd448..a90480f8015480 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sysfs.h" @@ -553,20 +554,22 @@ static void rpc_sysfs_xprt_release(struct kobject *kobj) kfree(xprt); } -static const void *rpc_sysfs_client_namespace(const struct kobject *kobj) +static const struct ns_common *rpc_sysfs_client_namespace(const struct kobject *kobj) { - return container_of(kobj, struct rpc_sysfs_client, kobject)->net; + return to_ns_common(container_of(kobj, struct rpc_sysfs_client, + kobject)->net); } -static const void *rpc_sysfs_xprt_switch_namespace(const struct kobject *kobj) +static const struct ns_common *rpc_sysfs_xprt_switch_namespace(const struct kobject *kobj) { - return container_of(kobj, struct rpc_sysfs_xprt_switch, kobject)->net; + return to_ns_common(container_of(kobj, struct rpc_sysfs_xprt_switch, + kobject)->net); } -static const void *rpc_sysfs_xprt_namespace(const struct kobject *kobj) +static const struct ns_common *rpc_sysfs_xprt_namespace(const struct kobject *kobj) { - return container_of(kobj, struct rpc_sysfs_xprt, - kobject)->xprt->xprt_net; + return to_ns_common(container_of(kobj, struct rpc_sysfs_xprt, + kobject)->xprt->xprt_net); } static struct kobj_attribute rpc_sysfs_clnt_version = __ATTR(rpc_version, diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 2e0ea69b9604e9..0b9abe70d39d86 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -154,11 +154,11 @@ static SIMPLE_DEV_PM_OPS(wiphy_pm_ops, wiphy_suspend, wiphy_resume); #define WIPHY_PM_OPS NULL #endif -static const void *wiphy_namespace(const struct device *d) +static const struct ns_common *wiphy_namespace(const struct device *d) { struct wiphy *wiphy = container_of(d, struct wiphy, dev); - return wiphy_net(wiphy); + return to_ns_common(wiphy_net(wiphy)); } struct class ieee80211_class = { From 1fe989e1c42a315c7e7918e7b812377137085036 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 1 Apr 2026 12:21:16 +0200 Subject: [PATCH 647/700] kernfs: use namespace id instead of pointer for hashing and comparison kernfs uses the namespace tag as both a hash seed (via init_name_hash()) and a comparison key in the rbtree. The resulting hash values are exposed to userspace through directory seek positions (ctx->pos), and the raw pointer comparisons in kernfs_name_compare() encode kernel pointer ordering into the rbtree layout. This constitutes a KASLR information leak since the hash and ordering derived from kernel pointers can be observed from userspace. Fix this by using the 64-bit namespace id (ns_common::ns_id) instead of the raw pointer value for both hashing and comparison. The namespace id is a stable, non-secret identifier that is already exposed to userspace through other interfaces (e.g., /proc/pid/ns/, ioctl NS_GET_NSID). Introduce kernfs_ns_id() as a helper that extracts the namespace id from a potentially-NULL ns_common pointer, returning 0 for the no-namespace case. All namespace equality checks in the directory iteration and dentry revalidation paths are also switched from pointer comparison to ns_id comparison for consistency. Signed-off-by: Christian Brauner --- fs/kernfs/dir.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index be262145ae0847..f5cf1d74b5f138 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "kernfs-internal.h" @@ -306,6 +307,18 @@ struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) return parent; } +/* + * kernfs_ns_id - return the namespace id for a given namespace + * @ns: namespace tag (may be NULL) + * + * Use the 64-bit namespace id instead of raw pointers for hashing + * and comparison to avoid leaking kernel addresses to userspace. + */ +static u64 kernfs_ns_id(const struct ns_common *ns) +{ + return ns ? ns->ns_id : 0; +} + /** * kernfs_name_hash - calculate hash of @ns + @name * @name: Null terminated string to hash @@ -316,7 +329,7 @@ struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) static unsigned int kernfs_name_hash(const char *name, const struct ns_common *ns) { - unsigned long hash = init_name_hash(ns); + unsigned long hash = init_name_hash(kernfs_ns_id(ns)); unsigned int len = strlen(name); while (len--) hash = partial_name_hash(*name++, hash); @@ -333,13 +346,16 @@ static unsigned int kernfs_name_hash(const char *name, static int kernfs_name_compare(unsigned int hash, const char *name, const struct ns_common *ns, const struct kernfs_node *kn) { + u64 ns_id = kernfs_ns_id(ns); + u64 kn_ns_id = kernfs_ns_id(kn->ns); + if (hash < kn->hash) return -1; if (hash > kn->hash) return 1; - if (ns < kn->ns) + if (ns_id < kn_ns_id) return -1; - if (ns > kn->ns) + if (ns_id > kn_ns_id) return 1; return strcmp(name, kernfs_rcu_name(kn)); } @@ -1203,7 +1219,7 @@ static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name, /* The kernfs node has been moved to a different namespace */ if (parent && kernfs_ns_enabled(parent) && - kernfs_info(dentry->d_sb)->ns != kn->ns) + kernfs_ns_id(kernfs_info(dentry->d_sb)->ns) != kernfs_ns_id(kn->ns)) goto out_bad; up_read(&root->kernfs_rwsem); @@ -1775,7 +1791,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, old_name = kernfs_rcu_name(kn); if (!new_name) new_name = old_name; - if ((old_parent == new_parent) && (kn->ns == new_ns) && + if ((old_parent == new_parent) && + (kernfs_ns_id(kn->ns) == kernfs_ns_id(new_ns)) && (strcmp(old_name, new_name) == 0)) goto out; /* nothing to rename */ @@ -1861,7 +1878,8 @@ static struct kernfs_node *kernfs_dir_pos(const struct ns_common *ns, } } /* Skip over entries which are dying/dead or in the wrong namespace */ - while (pos && (!kernfs_active(pos) || pos->ns != ns)) { + while (pos && (!kernfs_active(pos) || + kernfs_ns_id(pos->ns) != kernfs_ns_id(ns))) { struct rb_node *node = rb_next(&pos->rb); if (!node) pos = NULL; @@ -1882,7 +1900,8 @@ static struct kernfs_node *kernfs_dir_next_pos(const struct ns_common *ns, pos = NULL; else pos = rb_to_kn(node); - } while (pos && (!kernfs_active(pos) || pos->ns != ns)); + } while (pos && (!kernfs_active(pos) || + kernfs_ns_id(pos->ns) != kernfs_ns_id(ns))); } return pos; } From cb76a81c7cec37bdf525164561b02665cd763421 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 2 Apr 2026 09:12:11 +0200 Subject: [PATCH 648/700] kernfs: make directory seek namespace-aware The rbtree backing kernfs directories is ordered by (hash, ns_id, name) but kernfs_dir_pos() only searches by hash when seeking to a position during readdir. When two nodes from different namespaces share the same hash value, the binary search can land on a node in the wrong namespace. The subsequent skip-forward loop walks rb_next() and may overshoot the correct node, silently dropping an entry from the readdir results. With the recent switch from raw namespace pointers to public namespace ids as hash seeds, computing hash collisions became an offline operation. An unprivileged user could unshare into a new network namespace, create a single interface whose name-hash collides with a target entry in init_net, and cause a victim's seekdir/readdir on /sys/class/net to miss that entry. Fix this by extending the rbtree search in kernfs_dir_pos() to also compare namespace ids when hashes match. Since the rbtree is already ordered by (hash, ns_id, name), this makes the seek land directly in the correct namespace's range, eliminating the wrong-namespace overshoot. Signed-off-by: Christian Brauner --- fs/kernfs/dir.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index f5cf1d74b5f138..22a4dff2a3af5f 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -1866,6 +1866,7 @@ static struct kernfs_node *kernfs_dir_pos(const struct ns_common *ns, } if (!pos && (hash > 1) && (hash < INT_MAX)) { struct rb_node *node = parent->dir.children.rb_node; + u64 ns_id = kernfs_ns_id(ns); while (node) { pos = rb_to_kn(node); @@ -1873,6 +1874,10 @@ static struct kernfs_node *kernfs_dir_pos(const struct ns_common *ns, node = node->rb_left; else if (hash > pos->hash) node = node->rb_right; + else if (ns_id < kernfs_ns_id(pos->ns)) + node = node->rb_left; + else if (ns_id > kernfs_ns_id(pos->ns)) + node = node->rb_right; else break; } From 3fd0da4fd8851a7e62d009b7db6c4a05b092bc19 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 Apr 2026 06:52:39 +0100 Subject: [PATCH 649/700] net: lan966x: fix page_pool error handling in lan966x_fdma_rx_alloc_page_pool() page_pool_create() can return an ERR_PTR on failure. The return value is used unconditionally in the loop that follows, passing the error pointer through xdp_rxq_info_reg_mem_model() into page_pool_use_xdp_mem(), which dereferences it, causing a kernel oops. Add an IS_ERR check after page_pool_create() to return early on failure. Fixes: 11871aba1974 ("net: lan96x: Use page_pool API") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Link: https://patch.msgid.link/20260405055241.35767-2-devnexen@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 7b6369e43451d4..74851c63e46a74 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -91,6 +91,8 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) pp_params.dma_dir = DMA_BIDIRECTIONAL; rx->page_pool = page_pool_create(&pp_params); + if (unlikely(IS_ERR(rx->page_pool))) + return PTR_ERR(rx->page_pool); for (int i = 0; i < lan966x->num_phys_ports; ++i) { struct lan966x_port *port; From 076344a6ad9d1308faaed1402fdcfdda68b604ab Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 Apr 2026 06:52:40 +0100 Subject: [PATCH 650/700] net: lan966x: fix page pool leak in error paths lan966x_fdma_rx_alloc() creates a page pool but does not destroy it if the subsequent fdma_alloc_coherent() call fails, leaking the pool. Similarly, lan966x_fdma_init() frees the coherent DMA memory when lan966x_fdma_tx_alloc() fails but does not destroy the page pool that was successfully created by lan966x_fdma_rx_alloc(), leaking it. Add the missing page_pool_destroy() calls in both error paths. Fixes: 11871aba1974 ("net: lan96x: Use page_pool API") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Link: https://patch.msgid.link/20260405055241.35767-3-devnexen@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 74851c63e46a74..10773fe93d4d71 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -119,8 +119,10 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) return PTR_ERR(rx->page_pool); err = fdma_alloc_coherent(lan966x->dev, fdma); - if (err) + if (err) { + page_pool_destroy(rx->page_pool); return err; + } fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size), FDMA_DCB_STATUS_INTR); @@ -957,6 +959,7 @@ int lan966x_fdma_init(struct lan966x *lan966x) err = lan966x_fdma_tx_alloc(&lan966x->tx); if (err) { fdma_free_coherent(lan966x->dev, &lan966x->rx.fdma); + page_pool_destroy(lan966x->rx.page_pool); return err; } From 59c3d55a946cacdb4181600723c20ac4f4c20c84 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 Apr 2026 06:52:41 +0100 Subject: [PATCH 651/700] net: lan966x: fix use-after-free and leak in lan966x_fdma_reload() When lan966x_fdma_reload() fails to allocate new RX buffers, the restore path restarts DMA using old descriptors whose pages were already freed via lan966x_fdma_rx_free_pages(). Since page_pool_put_full_page() can release pages back to the buddy allocator, the hardware may DMA into memory now owned by other kernel subsystems. Additionally, on the restore path, the newly created page pool (if allocation partially succeeded) is overwritten without being destroyed, leaking it. Fix both issues by deferring the release of old pages until after the new allocation succeeds. Save the old page array before the allocation so old pages can be freed on the success path. On the failure path, the old descriptors, pages and page pool are all still valid, making the restore safe. Also ensure the restore path re-enables NAPI and wakes the netdev, matching the success path. Fixes: 89ba464fcf54 ("net: lan966x: refactor buffer reload function") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Link: https://patch.msgid.link/20260405055241.35767-4-devnexen@gmail.com Signed-off-by: Paolo Abeni --- .../ethernet/microchip/lan966x/lan966x_fdma.c | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 10773fe93d4d71..f8ce735a7fc090 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -812,9 +812,15 @@ static int lan966x_qsys_sw_status(struct lan966x *lan966x) static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) { + struct page *(*old_pages)[FDMA_RX_DCB_MAX_DBS]; struct page_pool *page_pool; struct fdma fdma_rx_old; - int err; + int err, i, j; + + old_pages = kmemdup(lan966x->rx.page, sizeof(lan966x->rx.page), + GFP_KERNEL); + if (!old_pages) + return -ENOMEM; /* Store these for later to free them */ memcpy(&fdma_rx_old, &lan966x->rx.fdma, sizeof(struct fdma)); @@ -825,7 +831,6 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) lan966x_fdma_stop_netdev(lan966x); lan966x_fdma_rx_disable(&lan966x->rx); - lan966x_fdma_rx_free_pages(&lan966x->rx); lan966x->rx.page_order = round_up(new_mtu, PAGE_SIZE) / PAGE_SIZE - 1; lan966x->rx.max_mtu = new_mtu; err = lan966x_fdma_rx_alloc(&lan966x->rx); @@ -833,6 +838,11 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) goto restore; lan966x_fdma_rx_start(&lan966x->rx); + for (i = 0; i < fdma_rx_old.n_dcbs; ++i) + for (j = 0; j < fdma_rx_old.n_dbs; ++j) + page_pool_put_full_page(page_pool, + old_pages[i][j], false); + fdma_free_coherent(lan966x->dev, &fdma_rx_old); page_pool_destroy(page_pool); @@ -840,12 +850,17 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) lan966x_fdma_wakeup_netdev(lan966x); napi_enable(&lan966x->napi); - return err; + kfree(old_pages); + return 0; restore: lan966x->rx.page_pool = page_pool; memcpy(&lan966x->rx.fdma, &fdma_rx_old, sizeof(struct fdma)); lan966x_fdma_rx_start(&lan966x->rx); + lan966x_fdma_wakeup_netdev(lan966x); + napi_enable(&lan966x->napi); + + kfree(old_pages); return err; } From 4f84e6caf38b05991b3b2afc0ddf4e48c2752d1d Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Thu, 9 Apr 2026 02:33:05 +0800 Subject: [PATCH 652/700] ALSA: usb-audio: Add quirk flags for Feaulle Rainbow Feaulle Rainbow is a wired USB-C dynamic in-ear monitor (IEM) featuring active noise cancellation (ANC). The supported sample rates are 48000Hz and 96000Hz at 16bit or 24bit, but it does not support reading the current sample rate and results in an error message printed to kmsg. Set QUIRK_FLAG_GET_SAMPLE_RATE to skip the sample rate check. Its playback mixer reports val = -15360/0/128. Setting -15360 (-60dB) mutes the playback, so QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE is needed. Add a quirk table entry matching VID/PID=0x0e0b/0xfa01 and applying the mentioned quirk flags, so that it can work properly. Quirky device sample: usb 7-1: New USB device found, idVendor=0e0b, idProduct=fa01, bcdDevice= 1.00 usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 7-1: Product: Feaulle Rainbow usb 7-1: Manufacturer: Generic usb 7-1: SerialNumber: 20210726905926 Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260409-feaulle-rainbow-v1-1-09179e09000d@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6cf85922b7bedf..519d9d1a2a4165 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2337,6 +2337,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ From 01f218d439acd5e129d214ea57e760ee2e34e869 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Apr 2026 16:37:29 +0200 Subject: [PATCH 653/700] ALSA: hda/alc269: Drop superfluous GPIO write at resume alc269_resume() has an extra code to write GPIO data, but this is basically already done in the standard alc_init(), hence it's superfluous. Let's drop the code. Since all external callers of alc_write_gpio_data() are gone after this, fold the only usage of alc_write_gpio_data() into the caller and drop the export as well. Link: https://patch.msgid.link/20260409143735.1412134-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 7 ------- sound/hda/codecs/realtek/realtek.c | 12 ++---------- sound/hda/codecs/realtek/realtek.h | 1 - 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 9a799919e0c82c..0a5eadec3ef6f7 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1004,13 +1004,6 @@ static int alc269_resume(struct hda_codec *codec) snd_hda_regmap_sync(codec); hda_call_check_power_status(codec, 0x01); - /* on some machine, the BIOS will clear the codec gpio data when enter - * suspend, and won't restore the data after resume, so we restore it - * in the driver. - */ - if (spec->gpio_data) - alc_write_gpio_data(codec); - if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index d9b2f1993eaf73..db365a746b1adc 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -99,15 +99,6 @@ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) } EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK"); -void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} -EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK"); - void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, bool on) { @@ -119,7 +110,8 @@ void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, else spec->gpio_data &= ~mask; if (oldval != spec->gpio_data) - alc_write_gpio_data(codec); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); } EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK"); diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h index 2ca15a6c2a0840..de95642bb6484e 100644 --- a/sound/hda/codecs/realtek/realtek.h +++ b/sound/hda/codecs/realtek/realtek.h @@ -168,7 +168,6 @@ void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw); * GPIO helpers */ void alc_setup_gpio(struct hda_codec *codec, unsigned int mask); -void alc_write_gpio_data(struct hda_codec *codec); void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, bool on); From c271b0815f45078342bc4e778683c86fdc45fde7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 9 Apr 2026 17:43:25 +0100 Subject: [PATCH 654/700] ASoC: SDCA: Correct kernel doc for sdca_irq_cleanup() Fix typo of function argument name in the kernel doc. Fixes: 0b8757b220f9 ("ASoC: SDCA: Unregister IRQ handlers on module remove") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604090800.koxM6j6O-lkp@intel.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260409164328.3999434-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 5cdabf8ae9da37..475aebcafe3f13 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -555,7 +555,7 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); /** * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function - * @sdev: Device pointer against which the sdca_interrupt_info was allocated. + * @dev: Device pointer against which the sdca_interrupt_info was allocated. * @function: Pointer to the SDCA Function. * @info: Pointer to the SDCA interrupt info for this device. * From 7936490e04733ade80d0d445529c0a6de0f95515 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Apr 2026 17:43:26 +0100 Subject: [PATCH 655/700] ASoC: SDCA: Fix cleanup inversion in class driver Fix inverted cleanup of the SoundWire IRQ and the function drivers that use it. The devm cleanup function to call sdca_dev_unregister_functions() was being registered at the end of class_sdw_probe(). The bus core creates the parent SoundWire IRQ handler after class_sdw_probe() has returned, and it registers a devm cleanup handler at the same time. This led to a cleanup inversion where the devm cleanup for the parent Soundwire IRQ runs before the handler that removes the function drivers. So the parent IRQ is destroyed before the function drivers had a chance to do any cleanup and remove their IRQ handlers. Move the registrations of the function driver cleanup into class_boot_work() after the function drivers are registered, so that it runs before the cleanup of the parent SoundWire IRQ handler. Fixes: 2d877d0659cb ("ASoC: SDCA: Add basic SDCA class driver") Signed-off-by: Richard Fitzgerald Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260409164328.3999434-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 918b638acb5774..5def6ae2d99f04 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -137,6 +137,13 @@ static const struct regmap_config class_dev_regmap_config = { .unlock = class_regmap_unlock, }; +static void class_remove_functions(void *data) +{ + struct sdca_class_drv *drv = data; + + sdca_dev_unregister_functions(drv->sdw); +} + static void class_boot_work(struct work_struct *work) { struct sdca_class_drv *drv = container_of(work, @@ -157,6 +164,11 @@ static void class_boot_work(struct work_struct *work) if (ret) goto err; + /* Ensure function drivers are removed before the IRQ is destroyed */ + ret = devm_add_action_or_reset(drv->dev, class_remove_functions, drv); + if (ret) + goto err; + dev_dbg(drv->dev, "boot work complete\n"); pm_runtime_mark_last_busy(drv->dev); @@ -168,15 +180,6 @@ static void class_boot_work(struct work_struct *work) pm_runtime_put_sync(drv->dev); } -static void class_dev_remove(void *data) -{ - struct sdca_class_drv *drv = data; - - cancel_work_sync(&drv->boot_work); - - sdca_dev_unregister_functions(drv->sdw); -} - static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id) { struct device *dev = &sdw->dev; @@ -230,15 +233,19 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id if (ret) return ret; - ret = devm_add_action_or_reset(dev, class_dev_remove, drv); - if (ret) - return ret; - queue_work(system_long_wq, &drv->boot_work); return 0; } +static void class_sdw_remove(struct sdw_slave *sdw) +{ + struct device *dev = &sdw->dev; + struct sdca_class_drv *drv = dev_get_drvdata(dev); + + cancel_work_sync(&drv->boot_work); +} + static int class_suspend(struct device *dev) { struct sdca_class_drv *drv = dev_get_drvdata(dev); @@ -328,6 +335,7 @@ static struct sdw_driver class_sdw_driver = { }, .probe = class_sdw_probe, + .remove = class_sdw_remove, .id_table = class_sdw_id, .ops = &class_sdw_ops, }; From 87ceac0a98e92f4efd031b5b9ab49ab5645d1c7e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 9 Apr 2026 17:43:27 +0100 Subject: [PATCH 656/700] ASoC: SDCA: Tidy up irq_enable_flags()/sdca_irq_disable() In irq_enable_flags() and sdca_irq_disable() there is a NULL check on the interrupt data pointer, however this is just pulled from an array so can never be NULL. This was likely left over from an earlier version that looked up the data in a different way. Replace the check with checking for the IRQ itself being non-zero. Whilst here also drop the sdca_interrupt structure down into the loop within the function to better match the style of the rest of the code in this file. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260409164328.3999434-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 475aebcafe3f13..3d7eb19967243a 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -630,13 +630,12 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); static void irq_enable_flags(struct sdca_function_data *function, struct sdca_interrupt_info *info, bool early) { - struct sdca_interrupt *interrupt; int i; for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { - interrupt = &info->irqs[i]; + struct sdca_interrupt *interrupt = &info->irqs[i]; - if (!interrupt || interrupt->function != function) + if (!interrupt->irq || interrupt->function != function) continue; switch (SDCA_CTL_TYPE(interrupt->entity->type, @@ -689,13 +688,12 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_enable, "SND_SOC_SDCA"); void sdca_irq_disable(struct sdca_function_data *function, struct sdca_interrupt_info *info) { - struct sdca_interrupt *interrupt; int i; for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { - interrupt = &info->irqs[i]; + struct sdca_interrupt *interrupt = &info->irqs[i]; - if (!interrupt || interrupt->function != function) + if (!interrupt->irq || interrupt->function != function) continue; disable_irq(interrupt->irq); From 833011cbe663b4ab49c125e09d02c900b599e13b Mon Sep 17 00:00:00 2001 From: Qinghua Zhao Date: Thu, 9 Apr 2026 21:52:13 +0800 Subject: [PATCH 657/700] ASoC: tegra: Fix spelling error 'recieved' -> 'received' Fix typo in comment where 'recieved' should be 'received'. Signed-off-by: Qinghua Zhao Link: https://patch.msgid.link/20260409135213.16558-1-zqh1630@126.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_spdif.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index ff4b79e2052fce..26ab8d5c7f99d8 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -171,7 +171,7 @@ /* * RX channel block data receive status: - * 0=entire block not recieved yet. + * 0=entire block not received yet. * 1=received entire block of channel status, */ #define TEGRA20_SPDIF_STATUS_IS_C (1 << 21) From fbb1f8ba4e2d847859d04220c4a775996f072d57 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 9 Apr 2026 14:01:01 +0800 Subject: [PATCH 658/700] ASoC: rt1320-sdw: kcontrol for brown-out feature update Create a kcontrol to enable or disable brown-out dynamically. Signed-off-by: Jack Yu Link: https://patch.msgid.link/20260409060102.4177554-1-jack.yu@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 42 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt1320-sdw.h | 1 + 2 files changed, 43 insertions(+) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 8bb7e8497c726e..168d2f39172356 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -2486,6 +2486,45 @@ static int rt1320_rae_update_put(struct snd_kcontrol *kcontrol, return 0; } +static int rt1320_brown_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + + if (!rt1320->hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (rt1320->brown_out != ucontrol->value.integer.value[0]) { + changed = 1; + rt1320->brown_out = ucontrol->value.integer.value[0]; + } + + if (rt1320->brown_out == 0) + regmap_write(rt1320->regmap, 0xdb03, 0x00); + else + regmap_write(rt1320->regmap, 0xdb03, 0xf0); + + + return changed; +} + +static int rt1320_brown_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1320->brown_out; + + return 0; +} + static int rt1320_r0_temperature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2545,6 +2584,8 @@ static const struct snd_kcontrol_new rt1320_snd_controls[] = { rt1320_r0_temperature_get, rt1320_r0_temperature_put), SOC_SINGLE_EXT("RAE Update", SND_SOC_NOPM, 0, 1, 0, rt1320_rae_update_get, rt1320_rae_update_put), + SOC_SINGLE_EXT("Brown Out Switch", SND_SOC_NOPM, 0, 1, 0, + rt1320_brown_out_get, rt1320_brown_out_put), }; static const struct snd_kcontrol_new rt1320_spk_l_dac = @@ -2904,6 +2945,7 @@ static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, rt1320->fu_dapm_mute = true; rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] = rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true; + rt1320->brown_out = 1; INIT_WORK(&rt1320->load_dspfw_work, rt1320_load_dspfw_work); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index 5a9f496dd848ff..4a1d3fc4c8eecd 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -159,6 +159,7 @@ struct rt1320_sdw_priv { bool hw_init; bool first_hw_init; int version_id; + int brown_out; unsigned int dev_id; bool fu_dapm_mute; bool fu_mixer_mute[4]; From 7d696210cf36ed31c7c37f6eff17cb7147e83367 Mon Sep 17 00:00:00 2001 From: Zxyan Zhu Date: Thu, 9 Apr 2026 11:50:15 +0800 Subject: [PATCH 659/700] regmap: debugfs: fix race condition in dummy name allocation Use IDA instead of a simple counter for generating unique dummy names. The previous implementation used dummy_index++ which is not atomic, leading to potential duplicate names when multiple threads call regmap_debugfs_init() concurrently with name="dummy". Signed-off-by: Zxyan Zhu Link: https://patch.msgid.link/20260409035015.950764-1-zxyan0222@gmail.com Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-debugfs.c | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c77f3a49a89e23..55273a6178f8d7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -84,6 +84,7 @@ struct regmap { bool debugfs_disable; struct dentry *debugfs; const char *debugfs_name; + int debugfs_dummy_id; unsigned int debugfs_reg_len; unsigned int debugfs_val_len; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 5a46ce5fee72a3..18f1c60749fed5 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "internal.h" @@ -20,7 +21,7 @@ struct regmap_debugfs_node { struct list_head link; }; -static unsigned int dummy_index; +static DEFINE_IDA(dummy_ida); static struct dentry *regmap_debugfs_root; static LIST_HEAD(regmap_debugfs_early_list); static DEFINE_MUTEX(regmap_debugfs_early_lock); @@ -539,6 +540,7 @@ void regmap_debugfs_init(struct regmap *map) struct regmap_range_node *range_node; const char *devname = "dummy"; const char *name = map->name; + int id; /* * Userspace can initiate reads from the hardware over debugfs. @@ -567,6 +569,7 @@ void regmap_debugfs_init(struct regmap *map) INIT_LIST_HEAD(&map->debugfs_off_cache); mutex_init(&map->cache_lock); + map->debugfs_dummy_id = -1; if (map->dev) devname = dev_name(map->dev); @@ -585,12 +588,16 @@ void regmap_debugfs_init(struct regmap *map) if (!strcmp(name, "dummy")) { kfree(map->debugfs_name); - map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", - dummy_index); - if (!map->debugfs_name) + id = ida_alloc(&dummy_ida, GFP_KERNEL); + if (id < 0) return; + map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", id); + if (!map->debugfs_name) { + ida_free(&dummy_ida, id); + return; + } + map->debugfs_dummy_id = id; name = map->debugfs_name; - dummy_index++; } map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); @@ -660,6 +667,10 @@ void regmap_debugfs_exit(struct regmap *map) mutex_lock(&map->cache_lock); regmap_debugfs_free_dump_cache(map); mutex_unlock(&map->cache_lock); + if (map->debugfs_dummy_id >= 0) { + ida_free(&dummy_ida, map->debugfs_dummy_id); + map->debugfs_dummy_id = -1; + } kfree(map->debugfs_name); map->debugfs_name = NULL; } else { From 57df6923ca53b524d06d2347b896d9de74b3bc86 Mon Sep 17 00:00:00 2001 From: Samasth Norway Ananda Date: Thu, 9 Apr 2026 11:58:53 -0700 Subject: [PATCH 660/700] gpio: tegra: return -ENOMEM on allocation failure in probe devm_kzalloc() failure in tegra_gpio_probe() returns -ENODEV, which indicates "no such device". The correct error code for a memory allocation failure is -ENOMEM. Signed-off-by: Samasth Norway Ananda Link: https://patch.msgid.link/20260409185853.2163034-1-samasth.norway.ananda@oracle.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index b14052fe64ac69..df06b56a2ade85 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -698,7 +698,7 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); if (!tgi) - return -ENODEV; + return -ENOMEM; tgi->soc = of_device_get_match_data(&pdev->dev); tgi->dev = &pdev->dev; From 0cb7aa965ad02e90ba7d6bf847f3de07e8d0c05e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 9 Apr 2026 23:39:31 +0200 Subject: [PATCH 661/700] ASoC: uda1380: Modernize the driver This codec driver depended on the legacy GPIO API, and nothing in the kernel is defining the platform data, so get rid of this. Two in-kernel device trees are defining this codec using undocumented device tree properties, so support these for now. The same properties can be defined using software nodes if board files are desired. The device tree use the "-gpio" rather than "-gpios" suffix but the GPIO DT parser will deal with that. Since there may be out of tree users, migrate to GPIO descriptors, drop the platform data that is unused, and assign the dac_clk the value that was used in all platforms found in a historical dig, and support setting the clock to the PLL using the undocumented device tree property. Add some menuconfig so the codec can be selected and tested. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260409-asoc-uda1380-v3-1-b3d5a53f31be@kernel.org Signed-off-by: Mark Brown --- include/sound/uda1380.h | 19 ----------- sound/soc/codecs/Kconfig | 6 ++-- sound/soc/codecs/uda1380.c | 65 ++++++++++++++++++++------------------ 3 files changed, 38 insertions(+), 52 deletions(-) delete mode 100644 include/sound/uda1380.h diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h deleted file mode 100644 index 2e42ea2d0cfd46..00000000000000 --- a/include/sound/uda1380.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * UDA1380 ALSA SoC Codec driver - * - * Copyright 2009 Philipp Zabel - */ - -#ifndef __UDA1380_H -#define __UDA1380_H - -struct uda1380_platform_data { - int gpio_power; - int gpio_reset; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - -#endif /* __UDA1380_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ca3e47db126e37..cf94a1c756e09e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2373,9 +2373,11 @@ config SND_SOC_UDA1342 mic inputs), stereo audio DAC, with basic audio processing. config SND_SOC_UDA1380 - tristate + tristate "Philips UDA1380 CODEC" depends on I2C - depends on GPIOLIB_LEGACY + help + The UDA1380 codec is used in the HTC Magician and on a number of + Samsung reference boards, as well as the LPC32xx series. config SND_SOC_WCD_CLASSH tristate diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 63c3ea878fcf2d..55c83d95bfba85 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -16,16 +16,19 @@ #include #include #include -#include +#include #include #include +#include #include #include #include #include #include #include -#include + +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 #include "uda1380.h" @@ -36,6 +39,8 @@ struct uda1380_priv { struct work_struct work; struct i2c_client *i2c; u16 *reg_cache; + struct gpio_desc *power; + struct gpio_desc *reset; }; /* @@ -169,13 +174,12 @@ static void uda1380_sync_cache(struct snd_soc_component *component) static int uda1380_reset(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata = component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(pdata->gpio_reset)) { - gpio_set_value(pdata->gpio_reset, 1); + if (uda1380->reset) { + gpiod_set_value(uda1380->reset, 1); mdelay(1); - gpio_set_value(pdata->gpio_reset, 0); + gpiod_set_value(uda1380->reset, 0); } else { u8 data[3]; @@ -608,9 +612,9 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int pm = uda1380_read_reg_cache(component, UDA1380_PM); int reg; - struct uda1380_platform_data *pdata = component->dev->platform_data; switch (level) { case SND_SOC_BIAS_ON: @@ -620,8 +624,8 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - if (gpio_is_valid(pdata->gpio_power)) { - gpio_set_value(pdata->gpio_power, 1); + if (uda1380->power) { + gpiod_set_value(uda1380->power, 1); mdelay(1); uda1380_reset(component); } @@ -631,10 +635,10 @@ static int uda1380_set_bias_level(struct snd_soc_component *component, uda1380_write(component, UDA1380_PM, 0x0); break; case SND_SOC_BIAS_OFF: - if (!gpio_is_valid(pdata->gpio_power)) + if (!uda1380->power) break; - gpio_set_value(pdata->gpio_power, 0); + gpiod_set_value(uda1380->power, 0); /* Mark mixer regs cache dirty to sync them with * codec regs on power on. @@ -713,13 +717,12 @@ static struct snd_soc_dai_driver uda1380_dai[] = { static int uda1380_probe(struct snd_soc_component *component) { - struct uda1380_platform_data *pdata =component->dev->platform_data; struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); int ret; uda1380->component = component; - if (!gpio_is_valid(pdata->gpio_power)) { + if (!uda1380->power) { ret = uda1380_reset(component); if (ret) return ret; @@ -728,7 +731,7 @@ static int uda1380_probe(struct snd_soc_component *component) INIT_WORK(&uda1380->work, uda1380_flush_work); /* set clock input */ - switch (pdata->dac_clk) { + switch (uda1380->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: uda1380_write_reg_cache(component, UDA1380_CLK, 0); break; @@ -760,31 +763,31 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = { static int uda1380_i2c_probe(struct i2c_client *i2c) { - struct uda1380_platform_data *pdata = i2c->dev.platform_data; + struct device *dev = &i2c->dev; struct uda1380_priv *uda1380; int ret; - if (!pdata) - return -EINVAL; - uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; - if (gpio_is_valid(pdata->gpio_reset)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, - GPIOF_OUT_INIT_LOW, "uda1380 reset"); - if (ret) - return ret; - } - - if (gpio_is_valid(pdata->gpio_power)) { - ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, - GPIOF_OUT_INIT_LOW, "uda1380 power"); - if (ret) - return ret; - } + uda1380->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->reset)) + return dev_err_probe(dev, PTR_ERR(uda1380->reset), + "error obtaining reset GPIO\n"); + gpiod_set_consumer_name(uda1380->reset, "uda1380 reset"); + + uda1380->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(uda1380->power)) + return dev_err_probe(dev, PTR_ERR(uda1380->power), + "error obtaining power GPIO\n"); + gpiod_set_consumer_name(uda1380->power, "uda1380 power"); + + /* This is just some default */ + uda1380->dac_clk = UDA1380_DAC_CLK_SYSCLK; + if (device_property_match_string(dev, "dac-clk", "wspll") >= 0) + uda1380->dac_clk = UDA1380_DAC_CLK_WSPLL; uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), sizeof(uda1380_reg[0]), GFP_KERNEL); From 72dcd84938f5026dc44d0e7e1e68d9d571c113a0 Mon Sep 17 00:00:00 2001 From: Ihor Uzlov Date: Fri, 10 Apr 2026 11:45:53 +0200 Subject: [PATCH 662/700] ASoC: amd: yc: Add MSI Vector A16 HX A8WHG to quirk table Add the MSI Vector A16 HX A8WHG (board MS-15MM) to the DMI quirk table to enable DMIC support. This laptop uses an AMD Ryzen 9 7945HX (Dragon Range) with the ACP6x audio coprocessor (rev 0x62) and a Realtek ALC274 codec. The built-in digital microphone is connected via the ACP PDM interface and requires this DMI entry to be activated. Tested on MSI Vector A16 HX A8WHG with kernel 6.8.0-107 (Ubuntu 24.04). DMIC capture device appears as 'acp6x' and records audio correctly. Signed-off-by: Ihor Uzlov Link: https://patch.msgid.link/20260410094553.24654-1-igor.uzlov@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 85579c81378fbe..c5cf45881416f3 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -500,6 +500,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), + } + }, { .driver_data = &acp6x_card, .matches = { From 8ad7f3b265a87cd4e5052677545f90f14c855b10 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 10 Apr 2026 10:29:24 +0800 Subject: [PATCH 663/700] regmap: i3c: Add non-devm regmap_init_i3c() helper Add __regmap_init_i3c() and the corresponding regmap_init_i3c() macro to allow creating a regmap for I3C devices without using the device-managed version. This mirrors the pattern already established for other buses such as I2C, SPI and so on, giving drivers more flexibility when the regmap lifetime is not directly tied to the device. Signed-off-by: Pei Xiao Link: https://patch.msgid.link/a81256a8866b163979a20406abf01df7d7440104.1775788105.git.xiaopei01@kylinos.cn Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i3c.c | 10 ++++++++++ include/linux/regmap.h | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c index 863b348704dcca..5af583d472ddd6 100644 --- a/drivers/base/regmap/regmap-i3c.c +++ b/drivers/base/regmap/regmap-i3c.c @@ -46,6 +46,16 @@ static const struct regmap_bus regmap_i3c = { .read = regmap_i3c_read, }; +struct regmap *__regmap_init_i3c(struct i3c_device *i3c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + return __regmap_init(&i3c->dev, ®map_i3c, &i3c->dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__regmap_init_i3c); + struct regmap *__devm_regmap_init_i3c(struct i3c_device *i3c, const struct regmap_config *config, struct lock_class_key *lock_key, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f1c5cb63c171a7..df44cb30f53b9f 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -693,6 +693,10 @@ struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, const struct regmap_sdw_mbq_cfg *mbq_config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *__regmap_init_i3c(struct i3c_device *i3c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); struct regmap *__regmap_init_spi_avmm(struct spi_device *spi, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -999,6 +1003,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); __regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \ dev, sdw, config, mbq_config) +/** + * regmap_init_i3c() - Initialise register map + * + * @i3c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +#define regmap_init_i3c(i3c, config) \ + __regmap_lockdep_wrapper(__regmap_init_i3c, #config, \ + i3c, config) + /** * regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave * to AVMM Bus Bridge From 558f5228e1dbfa995b7303e20f26836525e85151 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 10 Apr 2026 11:45:00 +0100 Subject: [PATCH 664/700] ASoC: SDCA: Update text of FIXME A couple of attempts to correct this FIXME have been sent upstream but the situation is not quite a simple as the FIXME implies. Update the FIXME to include a better description of the situation. Link: https://lore.kernel.org/linux-sound/20260408085607.3813488-1-shumingf@realtek.com/ Link: https://lore.kernel.org/linux-sound/20260324-sdca-function-status-init-irq-v1-1-bba49417a4e0@gmail.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260410104500.163337-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 3d7eb19967243a..6e10b4e660d96b 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -119,7 +119,17 @@ static irqreturn_t function_status_handler(int irq, void *data) for_each_set_bit(mask, &status, BITS_PER_BYTE) { switch (BIT(mask)) { case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION: - //FIXME: Add init writes +/* + * FIXME: Should this do init writes? + * + * Currently init writes/cache sync are done from the suspend/resume + * infrastructure. It is unclear in what situations one would receive this + * IRQ outside of that flow. Presumably it would be something like the chip + * crashing. In that case however doing the init writes and a cache sync might + * not be sufficient, for example if the failure was during audio playback + * there could be ordering constraints on the register writes to restore the + * state that are not handled by a simple cache sync. + */ break; case SDCA_CTL_ENTITY_0_FUNCTION_FAULT: dev_err(dev, "function fault\n"); From 2603ea46ce5c59568cf968ce35d2bf2854042182 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 10 Apr 2026 14:42:25 +0800 Subject: [PATCH 665/700] ASoC: rt1320-sdw: Add an approach to get new hardware advance gain Add an approach to get new hardware advance gain, and if there is no advance gain with this approach, we can still get advance gain with original method. Signed-off-by: Jack Yu Link: https://patch.msgid.link/20260410064225.662656-1-jack.yu@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 25 +++++++++++++++++++++++-- sound/soc/codecs/rt1320-sdw.h | 13 +++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 168d2f39172356..b0aeeab26bd99b 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1013,13 +1013,34 @@ static void rt1320_set_advancemode(struct rt1320_sdw_priv *rt1320) struct device *dev = &rt1320->sdw_slave->dev; struct rt1320_datafixpoint r0_data[2]; unsigned short l_advancegain, r_advancegain; + FwPara_Get_HwSwGain audDriverDataHwSwGain = {0}; + unsigned int HwAdvGain = 0; int ret; + /* Get new hardware advance gain by ID 1300 */ + ret = rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 1300, + &audDriverDataHwSwGain, sizeof(audDriverDataHwSwGain)); + if (ret == 0) { + HwAdvGain = audDriverDataHwSwGain.HwAdvGain; + dev_dbg(dev, "%s, HwAdvGain=%d\n", __func__, HwAdvGain); + dev_dbg(dev, "%s, HwBasGain=%d\n", __func__, audDriverDataHwSwGain.HwBasGain); + dev_dbg(dev, "%s, SwAdvGain=%d\n", __func__, audDriverDataHwSwGain.SwAdvGain); + dev_dbg(dev, "%s, SwBasGain=%d\n", __func__, audDriverDataHwSwGain.SwBasGain); + } else { + dev_dbg(dev, "%s: param 1300 not supported, ret=%d\n", __func__, ret); + } + /* Get advance gain/r0 */ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 6, &r0_data[0], sizeof(struct rt1320_datafixpoint)); rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 7, &r0_data[1], sizeof(struct rt1320_datafixpoint)); - l_advancegain = r0_data[0].advancegain; - r_advancegain = r0_data[1].advancegain; + + if (HwAdvGain != 0) { + l_advancegain = HwAdvGain & 0xffff; + r_advancegain = (HwAdvGain >> 16) & 0xffff; + } else { + l_advancegain = r0_data[0].advancegain; + r_advancegain = r0_data[1].advancegain; + } dev_dbg(dev, "%s, LR advanceGain=0x%x 0x%x\n", __func__, l_advancegain, r_advancegain); /* set R0 and enable protection by SetParameter id 6, 7 */ diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index 4a1d3fc4c8eecd..a7b573883dd04f 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -121,6 +121,19 @@ struct rt1320_datafixpoint { int invrs; }; +typedef struct FwPara_HwSwGain { + unsigned int SwAdvGain; + unsigned int SwBasGain; + unsigned int HwAdvGain; + unsigned int HwBasGain; + unsigned int reserve0; + unsigned int reserve1; + unsigned int reserve2; + unsigned int reserve3; + unsigned int reserve4; + unsigned int reserve5; +} __attribute__((packed)) FwPara_Get_HwSwGain; + struct rt1320_paramcmd { unsigned char moudleid; unsigned char commandtype; From c5b6285aae050ff1c3ea824ca3d88ac4be1e69c8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Mar 2026 14:52:41 +0000 Subject: [PATCH 666/700] ASoC: SOF: Don't allow pointer operations on unconfigured streams When reporting the pointer for a compressed stream we report the current I/O frame position by dividing the position by the number of channels multiplied by the number of container bytes. These values default to 0 and are only configured as part of setting the stream parameters so this allows a divide by zero to be configured. Validate that they are non zero, returning an error if not Fixes: c1a731c71359 ("ASoC: SOF: compress: Add support for computing timestamps") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260326-asoc-compress-tstamp-params-v1-1-3dc735b3d599@kernel.org Signed-off-by: Mark Brown --- sound/soc/sof/compress.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index f4c3e10e6344d4..93f2376585dbf5 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -381,6 +381,9 @@ static int sof_compr_pointer(struct snd_soc_component *component, if (!spcm) return -EINVAL; + if (!sstream->channels || !sstream->sample_container_bytes) + return -EBUSY; + tstamp->sampling_rate = sstream->sampling_rate; tstamp->copied_total = sstream->copied_total; tstamp->pcm_io_frames = div_u64(spcm->stream[cstream->direction].posn.dai_posn, From 48bd344e1040b9f2eb512be73c13f5db83efc191 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 16:01:56 +0200 Subject: [PATCH 667/700] ALSA: usx2y: us144mkii: fix NULL deref on missing interface 0 A malicious USB device with the TASCAM US-144MKII device id can have a configuration containing bInterfaceNumber=1 but no interface 0. USB configuration descriptors are not required to assign interface numbers sequentially, so usb_ifnum_to_if(dev, 0) returns will NULL, which will then be dereferenced directly. Fix this up by checking the return value properly. Cc: Jaroslav Kysela Cc: Takashi Iwai Fixes: dee1bcf28a3d ("ALSA: usb-audio: Add initial driver for TASCAM US-144MKII") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026040955-fall-gaining-e338@gregkh Signed-off-by: Takashi Iwai --- sound/usb/usx2y/us144mkii.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index 0cf4fa74e210ac..94553b61013c52 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -420,7 +420,11 @@ static int tascam_probe(struct usb_interface *intf, /* The device has two interfaces; we drive both from this driver. */ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { - tascam = usb_get_intfdata(usb_ifnum_to_if(dev, 0)); + struct usb_interface *intf_zero = usb_ifnum_to_if(dev, 0); + + if (!intf_zero) + return -ENODEV; + tascam = usb_get_intfdata(intf_zero); if (tascam) { usb_set_intfdata(intf, tascam); tascam->iface1 = intf; From 07704bbf36f57e4379e4cadf96410dab14621e3b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 16:05:54 +0200 Subject: [PATCH 668/700] ALSA: fireworks: bound device-supplied status before string array lookup The status field in an EFW response is a 32-bit value supplied by the firewire device. efr_status_names[] has 17 entries so a status value outside that range goes off into the weeds when looking at the %s value. Even worse, the status could return EFR_STATUS_INCOMPLETE which is 0x80000000, and is obviously not in that array of potential strings. Fix this up by properly bounding the index against the array size and printing "unknown" if it's not recognized. Cc: Clemens Ladisch Cc: Takashi Sakamoto Cc: Jaroslav Kysela Cc: Takashi Iwai Fixes: bde8a8f23bbe ("ALSA: fireworks: Add transaction and some commands") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/2026040953-astute-camera-1aa1@gregkh Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks_command.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c index 2b595ee0bc357b..05550f36fac551 100644 --- a/sound/firewire/fireworks/fireworks_command.c +++ b/sound/firewire/fireworks/fireworks_command.c @@ -151,10 +151,13 @@ efw_transaction(struct snd_efw *efw, unsigned int category, (be32_to_cpu(header->category) != category) || (be32_to_cpu(header->command) != command) || (be32_to_cpu(header->status) != EFR_STATUS_OK)) { + u32 st = be32_to_cpu(header->status); + dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n", be32_to_cpu(header->category), be32_to_cpu(header->command), - efr_status_names[be32_to_cpu(header->status)]); + st < ARRAY_SIZE(efr_status_names) ? + efr_status_names[st] : "unknown"); err = -EIO; goto end; } From b9c826916fdce6419b94eb0cd8810fdac18c2386 Mon Sep 17 00:00:00 2001 From: Berk Cem Goksel Date: Fri, 10 Apr 2026 08:13:41 +0300 Subject: [PATCH 669/700] ALSA: 6fire: fix use-after-free on disconnect In usb6fire_chip_abort(), the chip struct is allocated as the card's private data (via snd_card_new with sizeof(struct sfire_chip)). When snd_card_free_when_closed() is called and no file handles are open, the card and embedded chip are freed synchronously. The subsequent chip->card = NULL write then hits freed slab memory. Call trace: usb6fire_chip_abort sound/usb/6fire/chip.c:59 [inline] usb6fire_chip_disconnect+0x348/0x358 sound/usb/6fire/chip.c:182 usb_unbind_interface+0x1a8/0x88c drivers/usb/core/driver.c:458 ... hub_event+0x1a04/0x4518 drivers/usb/core/hub.c:5953 Fix by moving the card lifecycle out of usb6fire_chip_abort() and into usb6fire_chip_disconnect(). The card pointer is saved in a local before any teardown, snd_card_disconnect() is called first to prevent new opens, URBs are aborted while chip is still valid, and snd_card_free_when_closed() is called last so chip is never accessed after the card may be freed. Fixes: a0810c3d6dd2 ("ALSA: 6fire: Release resources at card release") Cc: stable@vger.kernel.org Cc: Andrey Konovalov Signed-off-by: Berk Cem Goksel Link: https://patch.msgid.link/20260410051341.1069716-1-berkcgoksel@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/6fire/chip.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 5ff78814e68750..874f6cd503ca59 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip) usb6fire_comm_abort(chip); if (chip->control) usb6fire_control_abort(chip); - if (chip->card) { - snd_card_disconnect(chip->card); - snd_card_free_when_closed(chip->card); - chip->card = NULL; - } } } @@ -168,6 +163,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf, static void usb6fire_chip_disconnect(struct usb_interface *intf) { struct sfire_chip *chip; + struct snd_card *card; chip = usb_get_intfdata(intf); if (chip) { /* if !chip, fw upload has been performed */ @@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf) chips[chip->regidx] = NULL; } + /* + * Save card pointer before teardown. + * snd_card_free_when_closed() may free card (and + * the embedded chip) immediately, so it must be + * called last and chip must not be accessed after. + */ + card = chip->card; chip->shutdown = true; + if (card) + snd_card_disconnect(card); usb6fire_chip_abort(chip); + if (card) + snd_card_free_when_closed(card); } } } From fb79bf127ac2577b4876132da6dba768018aad4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 10 Apr 2026 00:54:32 -0300 Subject: [PATCH 670/700] ALSA: sc6000: Keep the programmed board state in card-private data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver may auto-select IRQ and DMA resources at probe time, but sc6000_init_board() still derives the SC-6000 soft configuration from the module parameter arrays. When irq=auto or dma=auto is used, the codec is created with the selected resources while the board is programmed with the unresolved values. Store the mapped ports and generated SC-6000 board configuration in card-private data, build that configuration from the live probe results instead of the raw module parameters, and keep the probe-time board programming in a shared helper. This fixes the resource-programming mismatch and leaves the driver with a stable board-state block that can be reused by suspend/resume. Fixes: c282866101bf ("ALSA: sc6000: add support for SC-6600 and SC-7000") Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260410-alsa-sc6000-pm-v1-1-4d9e95493d26@gmail.com --- sound/isa/sc6000.c | 152 +++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 60 deletions(-) diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 6d618cc2ba4577..9949e06403f61a 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -100,6 +100,15 @@ MODULE_PARM_DESC(joystick, "Enable gameport."); #define PFX "sc6000: " #define DRV_NAME "SC-6000" +struct snd_sc6000 { + char __iomem *vport; + char __iomem *vmss_port; + u8 mss_config; + u8 config; + u8 hw_cfg[2]; + bool old_dsp; +}; + /* hardware dependent functions */ /* @@ -267,7 +276,7 @@ static int sc6000_dsp_reset(char __iomem *vport) /* detection and initialization */ static int sc6000_hw_cfg_write(struct device *devptr, - char __iomem *vport, const int *cfg) + char __iomem *vport, const u8 *cfg) { if (sc6000_write(devptr, vport, COMMAND_6C) < 0) { dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C); @@ -353,8 +362,7 @@ static int sc6000_init_mss(struct device *devptr, return 0; } -static void sc6000_hw_cfg_encode(struct device *devptr, - char __iomem *vport, int *cfg, +static void sc6000_hw_cfg_encode(struct device *devptr, u8 *cfg, long xport, long xmpu, long xmss_port, int joystick) { @@ -376,27 +384,83 @@ static void sc6000_hw_cfg_encode(struct device *devptr, dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]); } -static int sc6000_init_board(struct device *devptr, - char __iomem *vport, - char __iomem *vmss_port, int dev) +static void sc6000_prepare_board(struct device *devptr, + struct snd_sc6000 *sc6000, + unsigned int dev, int xirq, int xdma) +{ + sc6000->mss_config = sc6000_irq_to_softcfg(xirq) | + sc6000_dma_to_softcfg(xdma); + sc6000->config = sc6000->mss_config | + sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); + sc6000_hw_cfg_encode(devptr, sc6000->hw_cfg, port[dev], mpu_port[dev], + mss_port[dev], joystick[dev]); +} + +static void sc6000_detect_old_dsp(struct device *devptr, + struct snd_sc6000 *sc6000) +{ + sc6000_write(devptr, sc6000->vport, COMMAND_5C); + sc6000->old_dsp = sc6000_read(sc6000->vport) < 0; +} + +static int sc6000_program_board(struct device *devptr, + struct snd_sc6000 *sc6000) +{ + int err; + + if (!sc6000->old_dsp) { + if (sc6000_hw_cfg_write(devptr, sc6000->vport, + sc6000->hw_cfg) < 0) { + dev_err(devptr, "sc6000_hw_cfg_write: failed!\n"); + return -EIO; + } + } + + err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config); + if (err < 0) { + dev_err(devptr, "sc6000_setup_board: failed!\n"); + return -ENODEV; + } + + sc6000_dsp_reset(sc6000->vport); + + if (!sc6000->old_dsp) { + sc6000_write(devptr, sc6000->vport, COMMAND_60); + sc6000_write(devptr, sc6000->vport, 0x02); + sc6000_dsp_reset(sc6000->vport); + } + + err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config); + if (err < 0) { + dev_err(devptr, "sc6000_setup_board: failed!\n"); + return -ENODEV; + } + + err = sc6000_init_mss(devptr, sc6000->vport, sc6000->config, + sc6000->vmss_port, sc6000->mss_config); + if (err < 0) { + dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n"); + return -ENODEV; + } + + return 0; +} + +static int sc6000_init_board(struct device *devptr, struct snd_sc6000 *sc6000) { char answer[15]; char version[2]; - int mss_config = sc6000_irq_to_softcfg(irq[dev]) | - sc6000_dma_to_softcfg(dma[dev]); - int config = mss_config | - sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); int err; - int old = 0; - err = sc6000_dsp_reset(vport); + err = sc6000_dsp_reset(sc6000->vport); if (err < 0) { dev_err(devptr, "sc6000_dsp_reset: failed!\n"); return err; } memset(answer, 0, sizeof(answer)); - err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15); + err = sc6000_dsp_get_answer(devptr, sc6000->vport, GET_DSP_COPYRIGHT, + answer, 15); if (err <= 0) { dev_err(devptr, "sc6000_dsp_copyright: failed!\n"); return -ENODEV; @@ -408,54 +472,17 @@ static int sc6000_init_board(struct device *devptr, if (strncmp("SC-6000", answer, 7)) dev_warn(devptr, "Warning: non SC-6000 audio card!\n"); - if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) { + if (sc6000_dsp_get_answer(devptr, sc6000->vport, + GET_DSP_VERSION, version, 2) < 2) { dev_err(devptr, "sc6000_dsp_version: failed!\n"); return -ENODEV; } dev_info(devptr, "Detected model: %s, DSP version %d.%d\n", answer, version[0], version[1]); - /* set configuration */ - sc6000_write(devptr, vport, COMMAND_5C); - if (sc6000_read(vport) < 0) - old = 1; - - if (!old) { - int cfg[2]; - sc6000_hw_cfg_encode(devptr, - vport, &cfg[0], port[dev], mpu_port[dev], - mss_port[dev], joystick[dev]); - if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) { - dev_err(devptr, "sc6000_hw_cfg_write: failed!\n"); - return -EIO; - } - } - err = sc6000_setup_board(devptr, vport, config); - if (err < 0) { - dev_err(devptr, "sc6000_setup_board: failed!\n"); - return -ENODEV; - } - - sc6000_dsp_reset(vport); - - if (!old) { - sc6000_write(devptr, vport, COMMAND_60); - sc6000_write(devptr, vport, 0x02); - sc6000_dsp_reset(vport); - } + sc6000_detect_old_dsp(devptr, sc6000); - err = sc6000_setup_board(devptr, vport, config); - if (err < 0) { - dev_err(devptr, "sc6000_setup_board: failed!\n"); - return -ENODEV; - } - err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config); - if (err < 0) { - dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n"); - return -ENODEV; - } - - return 0; + return sc6000_program_board(devptr, sc6000); } static int snd_sc6000_mixer(struct snd_wss *chip) @@ -538,10 +565,10 @@ static int snd_sc6000_match(struct device *devptr, unsigned int dev) static void snd_sc6000_free(struct snd_card *card) { - char __iomem *vport = (char __force __iomem *)card->private_data; + struct snd_sc6000 *sc6000 = card->private_data; - if (vport) - sc6000_setup_board(card->dev, vport, 0); + if (sc6000->vport) + sc6000_setup_board(card->dev, sc6000->vport, 0); } static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) @@ -552,15 +579,17 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) int xirq = irq[dev]; int xdma = dma[dev]; struct snd_card *card; + struct snd_sc6000 *sc6000; struct snd_wss *chip; struct snd_opl3 *opl3; char __iomem *vport; char __iomem *vmss_port; err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE, - 0, &card); + sizeof(*sc6000), &card); if (err < 0) return err; + sc6000 = card->private_data; if (xirq == SNDRV_AUTO_IRQ) { xirq = snd_legacy_find_free_irq(possible_irqs); @@ -587,7 +616,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) dev_err(devptr, "I/O port cannot be iomapped.\n"); return -EBUSY; } - card->private_data = (void __force *)vport; + sc6000->vport = vport; /* to make it marked as used */ if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) { @@ -600,12 +629,15 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) dev_err(devptr, "MSS port I/O cannot be iomapped.\n"); return -EBUSY; } + sc6000->vmss_port = vmss_port; dev_dbg(devptr, "Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n", port[dev], xirq, xdma, mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); - err = sc6000_init_board(devptr, vport, vmss_port, dev); + sc6000_prepare_board(devptr, sc6000, dev, xirq, xdma); + + err = sc6000_init_board(devptr, sc6000); if (err < 0) return err; card->private_free = snd_sc6000_free; From 47f72d57ddb11222479c80bd07f5bc036d84c94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 10 Apr 2026 00:54:33 -0300 Subject: [PATCH 671/700] ALSA: sc6000: Restore board setup across suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_wss_resume() restores only the codec register image. The SC-6000 driver also programs card-specific DSP routing and enters MSS mode during probe, and that setup is not replayed after suspend. Cache the WSS chip pointer in the SC-6000 card state and wire ISA suspend and resume callbacks to the shared board-programming helper, so the board is reinitialized before the codec state is restored. This keeps the old/new DSP split in one place and restores the board-level MSS setup that the codec resume path does not cover. Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260410-alsa-sc6000-pm-v1-2-4d9e95493d26@gmail.com --- sound/isa/sc6000.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 9949e06403f61a..cd3a63c7c2a7e4 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -103,6 +103,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport."); struct snd_sc6000 { char __iomem *vport; char __iomem *vmss_port; + struct snd_wss *chip; u8 mss_config; u8 config; u8 hw_cfg[2]; @@ -646,6 +647,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) WSS_HW_DETECT, 0, &chip); if (err < 0) return err; + sc6000->chip = chip; err = snd_wss_pcm(chip, 0); if (err < 0) { @@ -702,10 +704,47 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev) return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev)); } +#ifdef CONFIG_PM +static int snd_sc6000_suspend(struct device *devptr, unsigned int dev, + pm_message_t state) +{ + struct snd_card *card = dev_get_drvdata(devptr); + struct snd_sc6000 *sc6000 = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + sc6000->chip->suspend(sc6000->chip); + return 0; +} + +static int snd_sc6000_resume(struct device *devptr, unsigned int dev) +{ + struct snd_card *card = dev_get_drvdata(devptr); + struct snd_sc6000 *sc6000 = card->private_data; + int err; + + err = sc6000_dsp_reset(sc6000->vport); + if (err < 0) { + dev_err(devptr, "sc6000_dsp_reset: failed!\n"); + return err; + } + + err = sc6000_program_board(devptr, sc6000); + if (err < 0) + return err; + + sc6000->chip->resume(sc6000->chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + static struct isa_driver snd_sc6000_driver = { .match = snd_sc6000_match, .probe = snd_sc6000_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_sc6000_suspend, + .resume = snd_sc6000_resume, +#endif .driver = { .name = DRV_NAME, }, From ba893caead54745595e29953f0531cf3651610aa Mon Sep 17 00:00:00 2001 From: David Arcari Date: Thu, 19 Mar 2026 10:03:07 -0400 Subject: [PATCH 672/700] tools/power turbostat: Allow execution to continue after perf_l2_init() failure Currently, if perf_l2_init() fails turbostat exits after issuing the following error (which was encountered on AlderLake): turbostat: perf_l2_init(cpu0, 0x0, 0xff24) REFS: Invalid argument This occurs because perf_l2_init() calls err(). However, the code has been written in such a manner that it is able to perform cleanup and continue. Therefore, this issue can be addressed by changing the appropriate calls to err() to warnx(). Additionally, correct the PMU type arguments passed to the warning strings in the ecore and lcore blocks so the logs accurately reflect the failing counter type. Signed-off-by: David Arcari Signed-off-by: Len Brown --- tools/power/x86/turbostat/turbostat.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 34e2143cd4b317..e9e8ef72395a94 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -9405,13 +9405,13 @@ void perf_l2_init(void) if (!is_hybrid) { fd_l2_percpu[cpu] = open_perf_counter(cpu, perf_pmu_types.uniform, perf_model_support->first.refs, -1, PERF_FORMAT_GROUP); if (fd_l2_percpu[cpu] == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.uniform, perf_model_support->first.refs); + warnx("%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.uniform, perf_model_support->first.refs); free_fd_l2_percpu(); return; } retval = open_perf_counter(cpu, perf_pmu_types.uniform, perf_model_support->first.hits, fd_l2_percpu[cpu], PERF_FORMAT_GROUP); if (retval == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.uniform, perf_model_support->first.hits); + warnx("%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.uniform, perf_model_support->first.hits); free_fd_l2_percpu(); return; } @@ -9420,39 +9420,39 @@ void perf_l2_init(void) if (perf_pcore_set && CPU_ISSET_S(cpu, cpu_possible_setsize, perf_pcore_set)) { fd_l2_percpu[cpu] = open_perf_counter(cpu, perf_pmu_types.pcore, perf_model_support->first.refs, -1, PERF_FORMAT_GROUP); if (fd_l2_percpu[cpu] == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->first.refs); + warnx("%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->first.refs); free_fd_l2_percpu(); return; } retval = open_perf_counter(cpu, perf_pmu_types.pcore, perf_model_support->first.hits, fd_l2_percpu[cpu], PERF_FORMAT_GROUP); if (retval == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->first.hits); + warnx("%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->first.hits); free_fd_l2_percpu(); return; } } else if (perf_ecore_set && CPU_ISSET_S(cpu, cpu_possible_setsize, perf_ecore_set)) { fd_l2_percpu[cpu] = open_perf_counter(cpu, perf_pmu_types.ecore, perf_model_support->second.refs, -1, PERF_FORMAT_GROUP); if (fd_l2_percpu[cpu] == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->second.refs); + warnx("%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.ecore, perf_model_support->second.refs); free_fd_l2_percpu(); return; } retval = open_perf_counter(cpu, perf_pmu_types.ecore, perf_model_support->second.hits, fd_l2_percpu[cpu], PERF_FORMAT_GROUP); if (retval == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->second.hits); + warnx("%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.ecore, perf_model_support->second.hits); free_fd_l2_percpu(); return; } } else if (perf_lcore_set && CPU_ISSET_S(cpu, cpu_possible_setsize, perf_lcore_set)) { fd_l2_percpu[cpu] = open_perf_counter(cpu, perf_pmu_types.lcore, perf_model_support->third.refs, -1, PERF_FORMAT_GROUP); if (fd_l2_percpu[cpu] == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->third.refs); + warnx("%s(cpu%d, 0x%x, 0x%llx) REFS", __func__, cpu, perf_pmu_types.lcore, perf_model_support->third.refs); free_fd_l2_percpu(); return; } retval = open_perf_counter(cpu, perf_pmu_types.lcore, perf_model_support->third.hits, fd_l2_percpu[cpu], PERF_FORMAT_GROUP); if (retval == -1) { - err(-1, "%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.pcore, perf_model_support->third.hits); + warnx("%s(cpu%d, 0x%x, 0x%llx) HITS", __func__, cpu, perf_pmu_types.lcore, perf_model_support->third.hits); free_fd_l2_percpu(); return; } From 52521e8398839105ef8eb22b3f0993f9b0d11a57 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 10 Apr 2026 16:32:19 +0200 Subject: [PATCH 673/700] ALSA: usb-audio: Evaluate packsize caps at the right place We introduced the upper bound checks of the packet sizes by the ep->maxframesize for avoiding the URB submission errors. However, the check was applied at an incorrect place in the function snd_usb_endpoint_set_params() where ep->maxframesize isn't defined yet; the value is defined at a bit later position. So this ended up with a failure at the first run while the second run works. For fixing it, move the check at the correct place, right after the calculation of ep->maxframesize in the same function. Fixes: 7fe8dec3f628 ("ALSA: usb-audio: Cap the packet size pre-calculations") Link: https://bugzilla.kernel.org/show_bug.cgi?id=221292 Cc: Link: https://patch.msgid.link/20260410143220.1676344-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index bf4401aba76ca8..6fbcb117555c6b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1379,9 +1379,6 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, return -EINVAL; } - ep->packsize[0] = min(ep->packsize[0], ep->maxframesize); - ep->packsize[1] = min(ep->packsize[1], ep->maxframesize); - /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; ep->freqshift = INT_MIN; @@ -1408,6 +1405,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes; ep->curframesize = ep->curpacksize / ep->cur_frame_bytes; + ep->packsize[0] = min(ep->packsize[0], ep->maxframesize); + ep->packsize[1] = min(ep->packsize[1], ep->maxframesize); + err = update_clock_ref_rate(chip, ep); if (err >= 0) { ep->need_setup = false; From b7feba842c0d5f6c5b01592f80d164e974767501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 10 Apr 2026 10:56:52 -0300 Subject: [PATCH 674/700] ALSA: interwave: guard PM-only restore helpers with CONFIG_PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The InterWave PM patch added snd_interwave_restore_regs() and snd_interwave_restore_memory() as static helpers, but both are used only from the resume path under CONFIG_PM. On configurations without CONFIG_PM, such as alpha allyesconfig, this leaves both helpers unused and triggers -Wunused-function warnings with W=1. Move the PM-only helpers into the existing CONFIG_PM section. Keep __snd_interwave_restore_regs() outside the guard because it is also used during probe-time initialization. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604101958.x16oNkfo-lkp@intel.com/ Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260410-alsa-interwave-pm-warning-fix-v1-1-434d14c9c262@gmail.com Signed-off-by: Takashi Iwai --- sound/isa/gus/interwave.c | 76 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 616c11e51a2f60..6c3a2977dcb3f1 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -480,44 +480,6 @@ static void __snd_interwave_restore_regs(struct snd_gus_card *gus) snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); } -static void snd_interwave_restore_regs(struct snd_gus_card *gus) -{ - scoped_guard(spinlock_irqsave, &gus->reg_lock) - __snd_interwave_restore_regs(gus); -} - -static void snd_interwave_restore_memory(struct snd_gus_card *gus) -{ - unsigned short mem_cfg; - unsigned int lmct = 0; - int i, lmc_cfg; - - if (!gus->gf1.memory) - return; - - for (i = 0; i < 4; i++) - lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8); - - lmc_cfg = snd_interwave_find_memory_config(lmct); - if (lmc_cfg < 0) { - if (!gus->gf1.enh_mode) { - lmc_cfg = 2; - } else { - dev_warn(gus->card->dev, - "cannot restore InterWave memory layout 0x%08x\n", - lmct); - return; - } - } - - scoped_guard(spinlock_irqsave, &gus->reg_lock) { - mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG); - mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg; - mem_cfg = (mem_cfg & 0xff1f) | (4 << 5); - snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg); - } -} - static void snd_interwave_init(int dev, struct snd_gus_card *gus) { /* Probe-time setup also clears the timer control register. */ @@ -888,6 +850,44 @@ static int snd_interwave_isa_probe(struct device *pdev, } #ifdef CONFIG_PM +static void snd_interwave_restore_regs(struct snd_gus_card *gus) +{ + scoped_guard(spinlock_irqsave, &gus->reg_lock) + __snd_interwave_restore_regs(gus); +} + +static void snd_interwave_restore_memory(struct snd_gus_card *gus) +{ + unsigned short mem_cfg; + unsigned int lmct = 0; + int i, lmc_cfg; + + if (!gus->gf1.memory) + return; + + for (i = 0; i < 4; i++) + lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8); + + lmc_cfg = snd_interwave_find_memory_config(lmct); + if (lmc_cfg < 0) { + if (!gus->gf1.enh_mode) { + lmc_cfg = 2; + } else { + dev_warn(gus->card->dev, + "cannot restore InterWave memory layout 0x%08x\n", + lmct); + return; + } + } + + scoped_guard(spinlock_irqsave, &gus->reg_lock) { + mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG); + mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg; + mem_cfg = (mem_cfg & 0xff1f) | (4 << 5); + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg); + } +} + static int snd_interwave_card_suspend(struct snd_card *card) { struct snd_interwave *iwcard = card->private_data; From 34fe4a9df2476f52a809d0cd9659ff73de605774 Mon Sep 17 00:00:00 2001 From: Abhinav Mahadevan Date: Fri, 10 Apr 2026 20:03:35 +0530 Subject: [PATCH 675/700] ALSA: usb-audio: Add quirk for PreSonus AudioBox USB The PreSonus AudioBox USB (0x194f:0x0301) only supports S24_3LE format for both playback and capture. It does not support S16_LE despite being a USB full-speed device. Add explicit format quirks for both the playback (interface 2) and capture (interface 3) interfaces to ensure correct format negotiation. Signed-off-by: Abhinav Mahadevan Link: https://patch.msgid.link/20260410143335.5974-1-abhi220204@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index b6dfe3b63c672b..803e03d4d77b88 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2652,6 +2652,54 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * The AudioBox USB advertises S24_3LE as the only supported format + * for both playback and capture. It does not support S16_LE despite + * being a USB full-speed device. + */ + USB_DEVICE(0x194f, 0x0301), + QUIRK_DRIVER_INFO { + .vendor_name = "PreSonus", + .product_name = "AudioBox USB", + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_IGNORE(0) }, + { + QUIRK_DATA_AUDIOFORMAT(2) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + } + }, + { + QUIRK_DATA_AUDIOFORMAT(3) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + } + }, + QUIRK_COMPOSITE_END + } + } +}, #endif /* disabled */ { From d6e152d905bdb1f32f9d99775e2f453350399a6a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Apr 2026 10:54:17 +0200 Subject: [PATCH 676/700] clockevents: Prevent timer interrupt starvation Calvin reported an odd NMI watchdog lockup which claims that the CPU locked up in user space. He provided a reproducer, which sets up a timerfd based timer and then rearms it in a loop with an absolute expiry time of 1ns. As the expiry time is in the past, the timer ends up as the first expiring timer in the per CPU hrtimer base and the clockevent device is programmed with the minimum delta value. If the machine is fast enough, this ends up in a endless loop of programming the delta value to the minimum value defined by the clock event device, before the timer interrupt can fire, which starves the interrupt and consequently triggers the lockup detector because the hrtimer callback of the lockup mechanism is never invoked. As a first step to prevent this, avoid reprogramming the clock event device when: - a forced minimum delta event is pending - the new expiry delta is less then or equal to the minimum delta Thanks to Calvin for providing the reproducer and to Borislav for testing and providing data from his Zen5 machine. The problem is not limited to Zen5, but depending on the underlying clock event device (e.g. TSC deadline timer on Intel) and the CPU speed not necessarily observable. This change serves only as the last resort and further changes will be made to prevent this scenario earlier in the call chain as far as possible. [ tglx: Updated to restore the old behaviour vs. !force and delta <= 0 and fixed up the tick-broadcast handlers as pointed out by Borislav ] Fixes: d316c57ff6bf ("[PATCH] clockevents: add core functionality") Reported-by: Calvin Owens Signed-off-by: Thomas Gleixner Tested-by: Calvin Owens Tested-by: Borislav Petkov Link: https://lore.kernel.org/lkml/acMe-QZUel-bBYUh@mozart.vkv.me/ Link: https://patch.msgid.link/20260407083247.562657657@kernel.org --- include/linux/clockchips.h | 2 ++ kernel/time/clockevents.c | 27 +++++++++++++++++++-------- kernel/time/hrtimer.c | 1 + kernel/time/tick-broadcast.c | 8 +++++++- kernel/time/tick-common.c | 1 + kernel/time/tick-sched.c | 1 + 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index b0df28ddd394be..50cdc9da8d32ac 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -80,6 +80,7 @@ enum clock_event_state { * @shift: nanoseconds to cycles divisor (power of two) * @state_use_accessors:current state of the device, assigned by the core code * @features: features + * @next_event_forced: True if the last programming was a forced event * @retries: number of forced programming retries * @set_state_periodic: switch state to periodic * @set_state_oneshot: switch state to oneshot @@ -108,6 +109,7 @@ struct clock_event_device { u32 shift; enum clock_event_state state_use_accessors; unsigned int features; + unsigned int next_event_forced; unsigned long retries; int (*set_state_periodic)(struct clock_event_device *); diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index eaae1ce9f06007..38570998a19b8c 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -172,6 +172,7 @@ void clockevents_shutdown(struct clock_event_device *dev) { clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN); dev->next_event = KTIME_MAX; + dev->next_event_forced = 0; } /** @@ -305,7 +306,6 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, { unsigned long long clc; int64_t delta; - int rc; if (WARN_ON_ONCE(expires < 0)) return -ETIME; @@ -324,16 +324,27 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, return dev->set_next_ktime(expires, dev); delta = ktime_to_ns(ktime_sub(expires, ktime_get())); - if (delta <= 0) - return force ? clockevents_program_min_delta(dev) : -ETIME; - delta = min(delta, (int64_t) dev->max_delta_ns); - delta = max(delta, (int64_t) dev->min_delta_ns); + /* Required for tick_periodic() during early boot */ + if (delta <= 0 && !force) + return -ETIME; + + if (delta > (int64_t)dev->min_delta_ns) { + delta = min(delta, (int64_t) dev->max_delta_ns); + clc = ((unsigned long long) delta * dev->mult) >> dev->shift; + if (!dev->set_next_event((unsigned long) clc, dev)) + return 0; + } - clc = ((unsigned long long) delta * dev->mult) >> dev->shift; - rc = dev->set_next_event((unsigned long) clc, dev); + if (dev->next_event_forced) + return 0; - return (rc && force) ? clockevents_program_min_delta(dev) : rc; + if (dev->set_next_event(dev->min_delta_ticks, dev)) { + if (!force || clockevents_program_min_delta(dev)) + return -ETIME; + } + dev->next_event_forced = 1; + return 0; } /* diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 860af7a584289a..1e37142fe52f4e 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1888,6 +1888,7 @@ void hrtimer_interrupt(struct clock_event_device *dev) BUG_ON(!cpu_base->hres_active); cpu_base->nr_events++; dev->next_event = KTIME_MAX; + dev->next_event_forced = 0; raw_spin_lock_irqsave(&cpu_base->lock, flags); entry_time = now = hrtimer_update_base(cpu_base); diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index f63c65881364df..7e57fa31ee26f1 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -76,8 +76,10 @@ const struct clock_event_device *tick_get_wakeup_device(int cpu) */ static void tick_broadcast_start_periodic(struct clock_event_device *bc) { - if (bc) + if (bc) { + bc->next_event_forced = 0; tick_setup_periodic(bc, 1); + } } /* @@ -403,6 +405,7 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev) bool bc_local; raw_spin_lock(&tick_broadcast_lock); + tick_broadcast_device.evtdev->next_event_forced = 0; /* Handle spurious interrupts gracefully */ if (clockevent_state_shutdown(tick_broadcast_device.evtdev)) { @@ -696,6 +699,7 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) raw_spin_lock(&tick_broadcast_lock); dev->next_event = KTIME_MAX; + tick_broadcast_device.evtdev->next_event_forced = 0; next_event = KTIME_MAX; cpumask_clear(tmpmask); now = ktime_get(); @@ -1063,6 +1067,7 @@ static void tick_broadcast_setup_oneshot(struct clock_event_device *bc, bc->event_handler = tick_handle_oneshot_broadcast; + bc->next_event_forced = 0; bc->next_event = KTIME_MAX; /* @@ -1175,6 +1180,7 @@ void hotplug_cpu__broadcast_tick_pull(int deadcpu) } /* This moves the broadcast assignment to this CPU: */ + bc->next_event_forced = 0; clockevents_program_event(bc, bc->next_event, 1); } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index d305d85218961b..6a9198a4279b5e 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -110,6 +110,7 @@ void tick_handle_periodic(struct clock_event_device *dev) int cpu = smp_processor_id(); ktime_t next = dev->next_event; + dev->next_event_forced = 0; tick_periodic(cpu); /* diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 36449f0010a452..d1f27df1e60ef2 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -1513,6 +1513,7 @@ static void tick_nohz_lowres_handler(struct clock_event_device *dev) struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); dev->next_event = KTIME_MAX; + dev->next_event_forced = 0; if (likely(tick_nohz_handler(&ts->sched_timer) == HRTIMER_RESTART)) tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); From 4f55a85cd4fc988712965f710ba1475e7ba3292a Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sat, 11 Apr 2026 01:49:02 +0800 Subject: [PATCH 677/700] ALSA: usb-audio: Add error checks against get_min_max*() All callers of get_min_max*() ignore the latter's return code completely. This means to ignore temporary errors at the probe time. However, it is not optimal and leads to some maintenance burdens. Return -EAGAIN for temporary errors, and check against it in the callers of get_min_max*(). If any other error occurs, bail out of the caller early. Suggested-by: Takashi Iwai Link: https://lore.kernel.org/r/87ldewi4j8.wl-tiwai@suse.de Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260411-uac-sticky-mixer-v1-1-29d62717befd@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index a25e8145af67e5..e5993364c8250a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1264,7 +1264,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->head.id, mixer_ctrl_intf(cval->head.mixer), cval->control, cval->head.id); - return -EINVAL; + return -EAGAIN; } if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, @@ -1388,6 +1388,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); + int ret; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) @@ -1398,8 +1399,9 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, if (cval->val_type != USB_MIXER_BOOLEAN && cval->val_type != USB_MIXER_INV_BOOLEAN) { if (!cval->initialized) { - get_min_max_with_quirks(cval, 0, kcontrol); - if (cval->initialized && cval->dBmin >= cval->dBmax) { + ret = get_min_max_with_quirks(cval, 0, kcontrol); + if ((ret >= 0 || ret == -EAGAIN) && + cval->initialized && cval->dBmin >= cval->dBmax) { kcontrol->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); @@ -1743,6 +1745,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; + int ret; if (control == UAC_FU_GRAPHIC_EQUALIZER) { /* FIXME: not supported yet */ @@ -1856,10 +1859,10 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, } /* get min/max values */ - get_min_max_with_quirks(cval, 0, kctl); + ret = get_min_max_with_quirks(cval, 0, kctl); /* skip a bogus volume range */ - if (cval->max <= cval->min) { + if ((ret < 0 && ret != -EAGAIN) || cval->max <= cval->min) { usb_audio_dbg(mixer->chip, "[%d] FU [%s] skipped due to invalid volume\n", cval->head.id, kctl->id.name); @@ -2233,6 +2236,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned int i, len; struct snd_kcontrol *kctl; const struct usbmix_name_map *map; + int ret; map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) @@ -2255,7 +2259,11 @@ static void build_mixer_unit_ctl(struct mixer_build *state, } /* get min/max values */ - get_min_max(cval, 0); + ret = get_min_max(cval, 0); + if (ret < 0 && ret != -EAGAIN) { + usb_mixer_elem_info_free(cval); + return; + } kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (!kctl) { @@ -2627,7 +2635,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, break; } - get_min_max(cval, valinfo->min_value); + err = get_min_max(cval, valinfo->min_value); break; } case USB_XU_CLOCK_RATE: @@ -2639,11 +2647,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval->max = 5; cval->res = 1; cval->initialized = 1; + err = 0; break; default: - get_min_max(cval, valinfo->min_value); + err = get_min_max(cval, valinfo->min_value); break; } + if (err < 0 && err != -EAGAIN) { + usb_mixer_elem_info_free(cval); + return err; + } err = get_cur_ctl_value(cval, cval->control << 8, &val); if (err < 0) { From e3ad86a82868fcde16f213240a60891c2d7bbec4 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sat, 11 Apr 2026 01:49:03 +0800 Subject: [PATCH 678/700] ALSA: usb-audio: Move volume control resolution check into a function get_min_max_with_quirks() is too lengthy and hard to read. Move the volume control resolution check code into a function as it's relatively self-contained. Suggested-by: Takashi Iwai Link: https://lore.kernel.org/r/87o6jsk3vs.wl-tiwai@suse.de Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260411-uac-sticky-mixer-v1-2-29d62717befd@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 65 ++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e5993364c8250a..e77c2d78a782a3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1232,6 +1232,38 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) snd_usb_set_cur_mix_value(cval, ch, idx, cval->min); } +/* + * Additional checks for the proper resolution + * + * Some devices report smaller resolutions than actually reacting. + * They don't return errors but simply clip to the lower aligned value. + */ +static void check_volume_control_res(struct usb_mixer_elem_info *cval, + int channel, int saved) +{ + int last_valid_res = cval->res; + int test, check; + + for (;;) { + test = saved; + if (test < cval->max) + test += cval->res; + else + test -= cval->res; + + if (test < cval->min || test > cval->max || + snd_usb_set_cur_mix_value(cval, channel, 0, test) || + get_cur_mix_raw(cval, channel, &check)) { + cval->res = last_valid_res; + break; + } + if (test == check) + break; + + cval->res *= 2; + } +} + /* * retrieve the minimum and maximum values for the specified control */ @@ -1287,37 +1319,18 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, if (cval->res == 0) cval->res = 1; - /* Additional checks for the proper resolution - * - * Some devices report smaller resolutions than actually - * reacting. They don't return errors but simply clip - * to the lower aligned value. - */ if (cval->min + cval->res < cval->max) { - int last_valid_res = cval->res; - int saved, test, check; + int saved; + if (get_cur_mix_raw(cval, minchn, &saved) < 0) - goto no_res_check; - for (;;) { - test = saved; - if (test < cval->max) - test += cval->res; - else - test -= cval->res; - if (test < cval->min || test > cval->max || - snd_usb_set_cur_mix_value(cval, minchn, 0, test) || - get_cur_mix_raw(cval, minchn, &check)) { - cval->res = last_valid_res; - break; - } - if (test == check) - break; - cval->res *= 2; - } + goto no_checks; + + check_volume_control_res(cval, minchn, saved); + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } -no_res_check: +no_checks: cval->initialized = 1; } From 86aa1ea1f15ce6b56ac1b4c0d9b88a07a5b9bf03 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sat, 11 Apr 2026 01:49:04 +0800 Subject: [PATCH 679/700] ALSA: usb-audio: Do not expose sticky mixers Some devices' mixers are sticky, which accept SET_CUR but do absolutely nothing. Registering these mixers confuses userspace and results in ineffective volume control. Check if a mixer is sticky by setting the volume to the maximum or minimum value and checking for effectiveness afterward. Prevent the mixer from being registered if it turns out to be sticky. Quirky device sample: usb 7-1: New USB device found, idVendor=0e0b, idProduct=fa01, bcdDevice= 1.00 usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 7-1: Product: Feaulle Rainbow usb 7-1: Manufacturer: Generic usb 7-1: SerialNumber: 20210726905926 (Mic Capture Volume) Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260411-uac-sticky-mixer-v1-3-29d62717befd@rong.moe Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 48 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e77c2d78a782a3..d4ef45bf53d77c 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1232,6 +1232,41 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) snd_usb_set_cur_mix_value(cval, ch, idx, cval->min); } +/* + * Additional checks for sticky mixers + * + * Some devices' volume control mixers are sticky, which accept SET_CUR but + * do absolutely nothing. + * + * Prevent sticky mixers from being registered, otherwise they confuses + * userspace and results in ineffective volume control. + */ +static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + int channel, int saved) +{ + int sticky_test_values[] = { cval->min, cval->max }; + int test, check, i; + + for (i = 0; i < ARRAY_SIZE(sticky_test_values); i++) { + test = sticky_test_values[i]; + if (test == saved) + continue; + + /* Assume non-sticky on failure. */ + if (snd_usb_set_cur_mix_value(cval, channel, 0, test) || + get_cur_mix_raw(cval, channel, &check) || + check != saved) /* SET_CUR effective, non-sticky. */ + return 0; + } + + usb_audio_err(cval->head.mixer->chip, + "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", + cval->head.id, mixer_ctrl_intf(cval->head.mixer), + cval->min, cval->max, cval->res, saved); + + return -ENODEV; +} + /* * Additional checks for the proper resolution * @@ -1270,7 +1305,7 @@ static void check_volume_control_res(struct usb_mixer_elem_info *cval, static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, int default_min, struct snd_kcontrol *kctl) { - int i, idx; + int i, idx, ret; /* for failsafe */ cval->min = default_min; @@ -1319,13 +1354,20 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, if (cval->res == 0) cval->res = 1; - if (cval->min + cval->res < cval->max) { + if (cval->min < cval->max) { int saved; if (get_cur_mix_raw(cval, minchn, &saved) < 0) goto no_checks; - check_volume_control_res(cval, minchn, saved); + ret = check_sticky_volume_control(cval, minchn, saved); + if (ret < 0) { + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); + return ret; + } + + if (cval->min + cval->res < cval->max) + check_volume_control_res(cval, minchn, saved); snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } From 31d00156e50ecad37f2cb6cbf04aaa9a260505ef Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 4 Apr 2026 08:29:58 +0800 Subject: [PATCH 680/700] crypto: af_alg - Fix page reassignment overflow in af_alg_pull_tsgl When page reassignment was added to af_alg_pull_tsgl the original loop wasn't updated so it may try to reassign one more page than necessary. Add the check to the reassignment so that this does not happen. Also update the comment which still refers to the obsolete offset argument. Reported-by: syzbot+d23888375c2737c17ba5@syzkaller.appspotmail.com Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management") Signed-off-by: Herbert Xu --- crypto/af_alg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 437f3e77c7e0e4..dd0e5be4d8c07a 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -705,8 +705,8 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst) * Assumption: caller created af_alg_count_tsgl(len) * SG entries in dst. */ - if (dst) { - /* reassign page to dst after offset */ + if (dst && plen) { + /* reassign page to dst */ get_page(page); sg_set_page(dst + j, page, plen, sg[i].offset); j++; From d702c3408213bb12bd570bb97204d8340d141c51 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 7 Apr 2026 12:58:18 +0200 Subject: [PATCH 681/700] X.509: Fix out-of-bounds access when parsing extensions Leo reports an out-of-bounds access when parsing a certificate with empty Basic Constraints or Key Usage extension because the first byte of the extension is read before checking its length. Fix it. The bug can be triggered by an unprivileged user by submitting a specially crafted certificate to the kernel through the keyrings(7) API. Leo has demonstrated this with a proof-of-concept program responsibly disclosed off-list. Fixes: 30eae2b037af ("KEYS: X.509: Parse Basic Constraints for CA") Fixes: 567671281a75 ("KEYS: X.509: Parse Key Usage") Reported-by: Leo Lin # off-list Signed-off-by: Lukas Wunner Reviewed-by: Ignat Korchagin Cc: stable@vger.kernel.org # v6.4+ Signed-off-by: Herbert Xu --- crypto/asymmetric_keys/x509_cert_parser.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 37e4fb9da10623..bfd10f0195e010 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -609,10 +609,10 @@ int x509_process_extension(void *context, size_t hdrlen, * 0x04 is where keyCertSign lands in this bit string * 0x80 is where digitalSignature lands in this bit string */ - if (v[0] != ASN1_BTS) - return -EBADMSG; if (vlen < 4) return -EBADMSG; + if (v[0] != ASN1_BTS) + return -EBADMSG; if (v[2] >= 8) return -EBADMSG; if (v[3] & 0x80) @@ -645,10 +645,10 @@ int x509_process_extension(void *context, size_t hdrlen, * (Expect 0xFF if the CA is TRUE) * vlen should match the entire extension size */ - if (v[0] != (ASN1_CONS_BIT | ASN1_SEQ)) - return -EBADMSG; if (vlen < 2) return -EBADMSG; + if (v[0] != (ASN1_CONS_BIT | ASN1_SEQ)) + return -EBADMSG; if (v[1] != vlen - 2) return -EBADMSG; /* Empty SEQUENCE means CA:FALSE (default value omitted per DER) */ From 3d14bd48e3a77091cbce637a12c2ae31b4a1687c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 12 Apr 2026 13:32:21 +0800 Subject: [PATCH 682/700] crypto: algif_aead - Fix minimum RX size check for decryption The check for the minimum receive buffer size did not take the tag size into account during decryption. Fix this by adding the required extra length. Reported-by: syzbot+aa11561819dc42ebbc7c@syzkaller.appspotmail.com Reported-by: Daniel Pouzzner Fixes: d887c52d6ae4 ("crypto: algif_aead - overhaul memory management") Signed-off-by: Herbert Xu --- crypto/algif_aead.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index dda15bb05e892d..f8bd45f7dc8392 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -144,7 +144,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, if (usedpages < outlen) { size_t less = outlen - usedpages; - if (used < less) { + if (used < less + (ctx->enc ? 0 : as)) { err = -EINVAL; goto free; } From f312f8b5988003a10d662904c58c8c6bc036782b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sat, 11 Apr 2026 15:14:40 -0300 Subject: [PATCH 683/700] ALSA: sscape: Cache per-card resources for board reinitialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoundScape driver programs the gate-array directly from the global resource arrays during probe. That is sufficient for initial bring-up, but a PM resume path also needs the resolved per-card IRQ, DMA, MPU IRQ and joystick settings after probe has finished. Store the resolved resources in struct soundscape and move the board setup into a reusable helper. Also factor the MIDI state programming so the same sequence can be reused by a later PM resume path. This is preparatory work for suspend/resume support and is not intended to change runtime behaviour. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260411-alsa-sscape-pm-v2-1-aeb5682e14b0@gmail.com Signed-off-by: Takashi Iwai --- sound/isa/sscape.c | 234 +++++++++++++++++++++++++++------------------ 1 file changed, 139 insertions(+), 95 deletions(-) diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index a31ca75774a6ae..6e951b3c0080cf 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -131,6 +131,11 @@ enum card_type { struct soundscape { spinlock_t lock; unsigned io_base; + unsigned long wss_base; + int irq; + int mpu_irq; + int dma1; + int dma2; int ic_type; enum card_type type; struct resource *io_res; @@ -138,6 +143,7 @@ struct soundscape { struct snd_wss *chip; unsigned char midi_vol; + bool joystick; struct device *dev; }; @@ -149,6 +155,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c) return (struct soundscape *) (c->private_data); } +/* + * Store the resolved board settings in the per-card state so that + * the same configuration can be replayed later if necessary. + */ +static void sscape_store_settings(struct soundscape *sscape, int dev) +{ + sscape->io_base = port[dev]; + sscape->wss_base = wss_port[dev]; + sscape->irq = irq[dev]; + sscape->mpu_irq = mpu_irq[dev]; + sscape->dma1 = dma[dev]; + sscape->dma2 = dma2[dev]; + sscape->joystick = joystick[dev]; +} + /* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to @@ -263,34 +284,36 @@ static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout) /* * Write to the SoundScape's host-mode control registers, but - * leave any locking issues to the caller ... + * leave any locking issues to the caller. Returns true if + * the write succeeded. */ -static inline int host_write_unsafe(unsigned io_base, unsigned char data) +static inline bool host_write_unsafe(unsigned int io_base, unsigned char data) { if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) { outb(data, HOST_DATA_IO(io_base)); - return 1; + return true; } - return 0; + return false; } /* * Write to the SoundScape's host-mode control registers, performing * a limited amount of busy-waiting if the register isn't ready. - * Also leaves all locking-issues to the caller ... + * Also leaves all locking-issues to the caller. Returns true if + * the write succeeded before timing out. */ -static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data, - unsigned timeout) +static bool host_write_ctrl_unsafe(unsigned int io_base, unsigned char data, + unsigned int timeout) { - int err; + bool written; - while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) { + while (!(written = host_write_unsafe(io_base, data)) && timeout != 0) { udelay(100); --timeout; } /* while */ - return err; + return written; } @@ -560,6 +583,30 @@ static int sscape_upload_microcode(struct snd_card *card, int version) return err; } +/* + * Restore the SoundScape's MIDI control state after the firmware + * upload has made the host interface available again. + */ +static int sscape_restore_midi_state(struct soundscape *sscape) +{ + bool success; + + guard(spinlock_irqsave)(&sscape->lock); + set_host_mode_unsafe(sscape->io_base); + + success = host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100) && + host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100) && + host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_SET_EXTMIDI, 100) && + host_write_ctrl_unsafe(sscape->io_base, 0, 100) && + host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100); + + set_midi_mode_unsafe(sscape->io_base); + + return success ? 0 : -EIO; +} + /* * Mixer control for the SoundScape's MIDI device. */ @@ -660,6 +707,59 @@ static unsigned get_irq_config(int sscape_type, int irq) return INVALID_IRQ; } +/* + * Program the SoundScape's board-specific routing and enable the + * codec path using the resolved IRQ, DMA and joystick settings. + */ +static int sscape_configure_board(struct soundscape *sscape) +{ + unsigned int dma_cfg; + unsigned int irq_cfg; + unsigned int mpu_irq_cfg; + int val; + + irq_cfg = get_irq_config(sscape->type, sscape->irq); + if (irq_cfg == INVALID_IRQ) + return -ENXIO; + + mpu_irq_cfg = get_irq_config(sscape->type, sscape->mpu_irq); + if (mpu_irq_cfg == INVALID_IRQ) + return -ENXIO; + + scoped_guard(spinlock_irqsave, &sscape->lock) { + if (sscape->ic_type == IC_OPUS) + activate_ad1845_unsafe(sscape->io_base); + + sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); + sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); + + /* + * Enable and configure the DMA channels ... + */ + sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); + dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70); + sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); + sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); + + mpu_irq_cfg |= mpu_irq_cfg << 2; + val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xf7; + if (sscape->joystick) + val |= 0x08; + sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0xd0); + sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, + 0xf0 | mpu_irq_cfg); + sscape_write_unsafe(sscape->io_base, GA_CDCFG_REG, + 0x09 | DMA_8BIT | + (sscape->dma1 << 4) | (irq_cfg << 1)); + /* + * Enable the master IRQ ... + */ + sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80); + } + + return 0; +} + /* * Perform certain arcane port-checks to see whether there * is a SoundScape board lurking behind the given ports. @@ -890,37 +990,33 @@ static int create_ad1845(struct snd_card *card, unsigned port, /* * Create an ALSA soundcard entry for the SoundScape, using - * the given list of port, IRQ and DMA resources. + * the resolved port, IRQ and DMA resources. */ -static int create_sscape(int dev, struct snd_card *card) +static int create_sscape(struct snd_card *card) { struct soundscape *sscape = get_card_soundscape(card); - unsigned dma_cfg; - unsigned irq_cfg; - unsigned mpu_irq_cfg; struct resource *io_res; struct resource *wss_res; int err; - int val; const char *name; /* * Grab IO ports that we will need to probe so that we * can detect and control this hardware ... */ - io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape"); + io_res = devm_request_region(card->dev, sscape->io_base, 8, "SoundScape"); if (!io_res) { dev_err(card->dev, - "sscape: can't grab port 0x%lx\n", port[dev]); + "sscape: can't grab port 0x%x\n", sscape->io_base); return -EBUSY; } wss_res = NULL; if (sscape->type == SSCAPE_VIVO) { - wss_res = devm_request_region(card->dev, wss_port[dev], 4, + wss_res = devm_request_region(card->dev, sscape->wss_base, 4, "SoundScape"); if (!wss_res) { dev_err(card->dev, "sscape: can't grab port 0x%lx\n", - wss_port[dev]); + sscape->wss_base); return -EBUSY; } } @@ -928,18 +1024,17 @@ static int create_sscape(int dev, struct snd_card *card) /* * Grab one DMA channel ... */ - err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape"); + err = snd_devm_request_dma(card->dev, sscape->dma1, "SoundScape"); if (err < 0) { - dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]); + dev_err(card->dev, "sscape: can't grab DMA %d\n", sscape->dma1); return err; } spin_lock_init(&sscape->lock); sscape->io_res = io_res; sscape->wss_res = wss_res; - sscape->io_base = port[dev]; - if (!detect_sscape(sscape, wss_port[dev])) { + if (!detect_sscape(sscape, sscape->wss_base)) { dev_err(card->dev, "sscape: hardware not detected at 0x%x\n", sscape->io_base); return -ENODEV; @@ -964,66 +1059,28 @@ static int create_sscape(int dev, struct snd_card *card) } dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n", - name, sscape->io_base, irq[dev], dma[dev]); - - /* - * Check that the user didn't pass us garbage data ... - */ - irq_cfg = get_irq_config(sscape->type, irq[dev]); - if (irq_cfg == INVALID_IRQ) { - dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]); - return -ENXIO; - } - - mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]); - if (mpu_irq_cfg == INVALID_IRQ) { - dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]); - return -ENXIO; - } + name, sscape->io_base, sscape->irq, sscape->dma1); /* * Tell the on-board devices where their resources are (I think - * I can't be sure without a datasheet ... So many magic values!) */ - scoped_guard(spinlock_irqsave, &sscape->lock) { - - sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); - sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); - - /* - * Enable and configure the DMA channels ... - */ - sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); - dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70); - sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); - sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); - - mpu_irq_cfg |= mpu_irq_cfg << 2; - val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7; - if (joystick[dev]) - val |= 8; - sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10); - sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg); - sscape_write_unsafe(sscape->io_base, - GA_CDCFG_REG, 0x09 | DMA_8BIT - | (dma[dev] << 4) | (irq_cfg << 1)); - /* - * Enable the master IRQ ... - */ - sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80); - + err = sscape_configure_board(sscape); + if (err < 0) { + dev_err(card->dev, "sscape: Invalid IRQ configuration\n"); + return err; } /* * We have now enabled the codec chip, and so we should * detect the AD1845 device ... */ - err = create_ad1845(card, wss_port[dev], irq[dev], - dma[dev], dma2[dev]); + err = create_ad1845(card, sscape->wss_base, sscape->irq, + sscape->dma1, sscape->dma2); if (err < 0) { dev_err(card->dev, "sscape: No AD1845 device at 0x%lx, IRQ %d\n", - wss_port[dev], irq[dev]); + sscape->wss_base, sscape->irq); return err; } strscpy(card->driver, "SoundScape"); @@ -1040,35 +1097,21 @@ static int create_sscape(int dev, struct snd_card *card) err = sscape_upload_microcode(card, err); if (err == 0) { - err = create_mpu401(card, MIDI_DEVNUM, port[dev], - mpu_irq[dev]); + err = create_mpu401(card, MIDI_DEVNUM, sscape->io_base, + sscape->mpu_irq); if (err < 0) { dev_err(card->dev, "sscape: Failed to create MPU-401 device at 0x%lx\n", - port[dev]); + (unsigned long)sscape->io_base); return err; } - /* - * Initialize mixer - */ - guard(spinlock_irqsave)(&sscape->lock); sscape->midi_vol = 0; - host_write_ctrl_unsafe(sscape->io_base, - CMD_SET_MIDI_VOL, 100); - host_write_ctrl_unsafe(sscape->io_base, - sscape->midi_vol, 100); - host_write_ctrl_unsafe(sscape->io_base, - CMD_XXX_MIDI_VOL, 100); - host_write_ctrl_unsafe(sscape->io_base, - sscape->midi_vol, 100); - host_write_ctrl_unsafe(sscape->io_base, - CMD_SET_EXTMIDI, 100); - host_write_ctrl_unsafe(sscape->io_base, - 0, 100); - host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100); - - set_midi_mode_unsafe(sscape->io_base); + err = sscape_restore_midi_state(sscape); + if (err < 0) + dev_warn(card->dev, + "sscape: MIDI init incomplete: %d\n", + err); } } @@ -1111,8 +1154,9 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev) sscape->type = SSCAPE; dma[dev] &= 0x03; + sscape_store_settings(sscape, dev); - ret = create_sscape(dev, card); + ret = create_sscape(card); if (ret < 0) return ret; @@ -1130,7 +1174,6 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev) static struct isa_driver snd_sscape_driver = { .match = snd_sscape_match, .probe = snd_sscape_probe, - /* FIXME: suspend/resume */ .driver = { .name = DEV_NAME }, @@ -1211,8 +1254,9 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard, wss_port[idx] = pnp_port_start(dev, 1); dma2[idx] = pnp_dma(dev, 1); } + sscape_store_settings(sscape, idx); - ret = create_sscape(idx, card); + ret = create_sscape(card); if (ret < 0) return ret; From 713e0f011178a2896e46db3244093454708066e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sat, 11 Apr 2026 15:14:41 -0300 Subject: [PATCH 684/700] ALSA: sscape: Add suspend and resume support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoundScape ISA driver has lacked suspend and resume callbacks since commit 277e926c9b27 ("[ALSA] sscape - Use platform_device"). A plain snd_wss resume is not sufficient for SoundScape. Resume also needs to restore the board-specific gate-array routing, and non-VIVO boards need to reinitialize the probe-time MIDI firmware and MIDI control state when the MPU-401 side was enabled during probe. That firmware reload can be handled in-kernel because commit acd47100914b ("ALSA: sscape: convert to firmware loader framework") moved the driver to request_firmware(). Add ISA and ISA-PnP PM callbacks, reconfigure the board on resume, reload the non-VIVO MIDI firmware, restore the MIDI state, and then resume the WSS codec. If MIDI firmware reload fails, keep the WSS resume path alive and leave MIDI unavailable instead of failing the whole device resume. Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260411-alsa-sscape-pm-v2-2-aeb5682e14b0@gmail.com Signed-off-by: Takashi Iwai --- sound/isa/sscape.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 6e951b3c0080cf..553ceb92d2989a 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -144,6 +144,7 @@ struct soundscape { unsigned char midi_vol; bool joystick; + bool midi_enabled; struct device *dev; }; @@ -1107,6 +1108,7 @@ static int create_sscape(struct snd_card *card) } sscape->midi_vol = 0; + sscape->midi_enabled = true; err = sscape_restore_midi_state(sscape); if (err < 0) dev_warn(card->dev, @@ -1118,6 +1120,77 @@ static int create_sscape(struct snd_card *card) return 0; } +#ifdef CONFIG_PM +/* + * Reload the MIDI firmware and restore the saved MIDI state for + * boards whose MPU-401 side was enabled during probe. + */ +static int sscape_resume_midi(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + int err, version; + + if (!sscape->midi_enabled) + return 0; + + version = sscape_upload_bootblock(card); + if (version < 0) + return version; + + err = sscape_upload_microcode(card, version); + if (err < 0) + return err; + + outb(0, sscape->io_base); + + return sscape_restore_midi_state(sscape); +} + +/* + * Save the WSS codec state before the SoundScape is suspended. + */ +static int snd_sscape_suspend_card(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + sscape->chip->suspend(sscape->chip); + return 0; +} + +/* + * Restore the board-specific state before resuming the WSS codec. + */ +static int snd_sscape_resume_card(struct snd_card *card) +{ + struct soundscape *sscape = get_card_soundscape(card); + int err; + + err = sscape_configure_board(sscape); + if (err < 0) + return err; + + err = sscape_resume_midi(card); + if (err < 0) + dev_warn(card->dev, "sscape: MIDI restore failed: %d\n", err); + + sscape->chip->resume(sscape->chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_sscape_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + return snd_sscape_suspend_card(dev_get_drvdata(dev)); +} + +static int snd_sscape_resume(struct device *dev, unsigned int n) +{ + return snd_sscape_resume_card(dev_get_drvdata(dev)); +} +#endif + static int snd_sscape_match(struct device *pdev, unsigned int i) { @@ -1174,6 +1247,10 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev) static struct isa_driver snd_sscape_driver = { .match = snd_sscape_match, .probe = snd_sscape_probe, +#ifdef CONFIG_PM + .suspend = snd_sscape_suspend, + .resume = snd_sscape_resume, +#endif .driver = { .name = DEV_NAME }, @@ -1271,11 +1348,27 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int sscape_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) +{ + return snd_sscape_suspend_card(pnp_get_card_drvdata(pcard)); +} + +static int sscape_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_sscape_resume_card(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver sscape_pnpc_driver = { .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, .name = "sscape", .id_table = sscape_pnpids, .probe = sscape_pnp_detect, +#ifdef CONFIG_PM + .suspend = sscape_pnp_suspend, + .resume = sscape_pnp_resume, +#endif }; #endif /* CONFIG_PNP */ From 115e7d764dad66a10e150bd4b3ba3bbb95b04d85 Mon Sep 17 00:00:00 2001 From: Anushka Badhe Date: Fri, 10 Apr 2026 11:25:32 +0530 Subject: [PATCH 685/700] ASoC: dt-bindings: rockchip: convert rk3399-gru-sound to DT Schema Convert the rockchip,rk3399-gru-sound.txt DT binding to DT Schema format. Update rockchip,cpu from a single I2S controller phandle to a phandle-array. Add an optional second entry for the SPDIF controller, as seen in rk3399-gru.dtsi, required by boards with DisplayPort audio. Signed-off-by: Anushka Badhe Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260410055532.60868-1-anushkabadhe@gmail.com Signed-off-by: Mark Brown --- .../sound/rockchip,rk3399-gru-sound.txt | 22 ------- .../sound/rockchip,rk3399-gru-sound.yaml | 60 +++++++++++++++++++ 2 files changed, 60 insertions(+), 22 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt deleted file mode 100644 index 72d3cf4c260600..00000000000000 --- a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt +++ /dev/null @@ -1,22 +0,0 @@ -ROCKCHIP with MAX98357A/RT5514/DA7219 codecs on GRU boards - -Required properties: -- compatible: "rockchip,rk3399-gru-sound" -- rockchip,cpu: The phandle of the Rockchip I2S controller that's - connected to the codecs -- rockchip,codec: The phandle of the audio codecs - -Optional properties: -- dmic-wakeup-delay-ms : specify delay time (ms) for DMIC ready. - If this option is specified, which means it's required dmic need - delay for DMIC to ready so that rt5514 can avoid recording before - DMIC send valid data - -Example: - -sound { - compatible = "rockchip,rk3399-gru-sound"; - rockchip,cpu = <&i2s0>; - rockchip,codec = <&max98357a &rt5514 &da7219>; - dmic-wakeup-delay-ms = <20>; -}; diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml new file mode 100644 index 00000000000000..e9d13695cc774c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip,rk3399-gru-sound.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip with MAX98357A/RT5514/DA7219 codecs on GRU boards + +maintainers: + - Heiko Stuebner + +properties: + compatible: + const: rockchip,rk3399-gru-sound + + rockchip,cpu: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles to the Rockchip CPU DAI controllers connected to codecs + minItems: 1 + items: + - items: + - description: Phandle to the Rockchip I2S controllers + - items: + - description: | + Phandle to the Rockchip SPDIF controller. Required when a + DisplayPort audio codec is referenced in rockchip,codec + + rockchip,codec: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + The phandles of the audio codecs connected to the Rockchip CPU DAI + controllers + minItems: 1 + maxItems: 6 + items: + maxItems: 1 + + dmic-wakeup-delay-ms: + description: | + specify delay time (ms) for DMIC ready. + If this option is specified, a delay is required for DMIC to get ready + so that rt5514 can avoid recording before DMIC sends valid data + +required: + - compatible + - rockchip,cpu + - rockchip,codec + +additionalProperties: false + +examples: + - | + sound { + compatible = "rockchip,rk3399-gru-sound"; + rockchip,cpu = <&i2s0 &spdif>; + rockchip,codec = <&max98357a &rt5514 &da7219 &cdn_dp>; + dmic-wakeup-delay-ms = <20>; + }; + From 028ef9c96e96197026887c0f092424679298aae8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 12 Apr 2026 13:48:06 -0700 Subject: [PATCH 686/700] Linux 7.0 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4f54c568563864..36d0a32fbe49be 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 0 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Baby Opossum Posse # *DOCUMENTATION* From 80bb50e2d459213cccff3111d5ef98ed4238c0d5 Mon Sep 17 00:00:00 2001 From: Berk Cem Goksel Date: Mon, 13 Apr 2026 06:49:41 +0300 Subject: [PATCH 687/700] ALSA: caiaq: take a reference on the USB device in create_card() The caiaq driver stores a pointer to the parent USB device in cdev->chip.dev but never takes a reference on it. The card's private_free callback, snd_usb_caiaq_card_free(), can run asynchronously via snd_card_free_when_closed() after the USB device has already been disconnected and freed, so any access to cdev->chip.dev in that path dereferences a freed usb_device. On top of the refcounting issue, the current card_free implementation calls usb_reset_device(cdev->chip.dev). A reset in a free callback is inappropriate: the device is going away, the call takes the device lock in a teardown context, and the reset races with the disconnect path that the callback is already cleaning up after. Take a reference on the USB device in create_card() with usb_get_dev(), drop it with usb_put_dev() in the free callback, and remove the usb_reset_device() call. Fixes: b04dcbb7f7b1 ("ALSA: caiaq: Use snd_card_free_when_closed() at disconnection") Cc: stable@vger.kernel.org Cc: Andrey Konovalov Signed-off-by: Berk Cem Goksel Link: https://patch.msgid.link/20260413034941.1131465-3-berkcgoksel@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/caiaq/device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 3a71bab8a47749..51177ebfb8c627 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -384,7 +384,7 @@ static void card_free(struct snd_card *card) snd_usb_caiaq_input_free(cdev); #endif snd_usb_caiaq_audio_free(cdev); - usb_reset_device(cdev->chip.dev); + usb_put_dev(cdev->chip.dev); } static int create_card(struct usb_device *usb_dev, @@ -410,7 +410,7 @@ static int create_card(struct usb_device *usb_dev, return err; cdev = caiaqdev(card); - cdev->chip.dev = usb_dev; + cdev->chip.dev = usb_get_dev(usb_dev); cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); From 882321ccaeea52dd645dff98bfea2f92b286e673 Mon Sep 17 00:00:00 2001 From: songxiebing Date: Mon, 13 Apr 2026 09:18:54 +0800 Subject: [PATCH 688/700] ALSA: hda/realtek: Add quirk for HP Spectre x360 14-ea HP Spectre x360 Convertible 14-ea0xxx (2021 model or so) doesn't make produce sound,The Bang & Olufsen speaker amplifier is not enabled. Root causing: The PCI subsystem ID is 103c:0000 (HP left it unset), while the codec subsystem ID is 103c:885b. The vendor-wide catch-all SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED) matches 103c:0000 before the codec SSID fallback is reached, so ALC245_FIXUP_HP_X360_AMP never applies. So add the quirk in alc269_fixup_tbl. Reported-by: dzidmail Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221341 Signed-off-by: songxiebing Link: https://patch.msgid.link/20260413011854.96520-1-songxiebing@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 0a5eadec3ef6f7..2cf1cc3cb91caa 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6947,6 +6947,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + HDA_CODEC_QUIRK(0x103c, 0x885b, "HP Spectre x360 14-ea", ALC245_FIXUP_HP_X360_AMP), SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), From 67f4c61a73e9b17dc9593bf27badc6785ecadd78 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Mon, 13 Apr 2026 23:48:17 +0800 Subject: [PATCH 689/700] ALSA: hda/realtek: Add quirk for Legion S7 15IMH Fix speaker output on the Lenovo Legion S7 15IMH05. Cc: stable@vger.kernel.org Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260413154818.351597-1-dnaim@cachyos.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2cf1cc3cb91caa..45bbd63370d6e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7607,6 +7607,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3811, "Legion S7 15IMH05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), From bc9b1ebaa7624edde54d4ae842d11cebb26db09b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 13 Apr 2026 09:00:45 +0200 Subject: [PATCH 690/700] ASoC: tas2781: fix unused-const-variable warning When both CONFIG_OF and CONFIG_ACPI are disabled, the ID table is not referenced any more: sound/soc/codecs/tas2781-i2c.c:102:35: error: 'tasdevice_id' defined but not used [-Werror=unused-const-variable=] 102 | static const struct i2c_device_id tasdevice_id[] = { | ^~~~~~~~~~~~ Remove the #ifdef checks and just include the ID tables unconditionally to get a clean build in all configurations. The code already uses IS_ENABLED() checks for both to benefit from dead code elimination and the ID tables are small enough that they can just be included all the time. Fixes: 9a52d1b7cb4a ("ASoC: tas2781: Explicit association of Device, Device Name, and Device ID") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260413070059.3828364-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c593f9da0c5b9b..8af30f4d68da7c 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -122,7 +122,6 @@ static const struct i2c_device_id tasdevice_id[] = { {} }; -#ifdef CONFIG_OF static const struct of_device_id tasdevice_of_match[] = { { .compatible = "ti,tas2020", .data = &tasdevice_id[TAS2020] }, { .compatible = "ti,tas2118", .data = &tasdevice_id[TAS2118] }, @@ -146,7 +145,6 @@ static const struct of_device_id tasdevice_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, tasdevice_of_match); -#endif /** * tas2781_digital_getvol - get the volum control @@ -2083,7 +2081,6 @@ static void tasdevice_i2c_remove(struct i2c_client *client) tasdevice_remove(tas_priv); } -#ifdef CONFIG_ACPI static const struct acpi_device_id tasdevice_acpi_match[] = { { "TXNW2020", (kernel_ulong_t)&tasdevice_id[TAS2020] }, { "TXNW2118", (kernel_ulong_t)&tasdevice_id[TAS2118] }, @@ -2108,15 +2105,12 @@ static const struct acpi_device_id tasdevice_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); -#endif static struct i2c_driver tasdevice_i2c_driver = { .driver = { .name = "tasdev-codec", - .of_match_table = of_match_ptr(tasdevice_of_match), -#ifdef CONFIG_ACPI - .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), -#endif + .of_match_table = tasdevice_of_match, + .acpi_match_table = tasdevice_acpi_match, }, .probe = tasdevice_i2c_probe, .remove = tasdevice_i2c_remove, From 54a032d3e62fd1792cb16d8096aaf00397589c5f Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 13 Apr 2026 18:52:43 +0800 Subject: [PATCH 691/700] ASoC: pxa2xx-ac97: fix error handling for reset GPIO descriptor The reset GPIO obtained via devm_gpiod_get() may return an ERR_PTR() when the GPIO is missing or an error occurs. The current code unconditionally assigns PTR_ERR() to ret and later dereferences rst_gpio via desc_to_gpio(), which is incorrect when rst_gpio is an error pointer. Rework the logic to first check IS_ERR(rst_gpio) before converting the descriptor. Handle -ENOENT by disabling reset GPIO support, and return other errors to the caller as expected. Fixes: c76d50b71e89 ("ASoC: ac97: Convert to GPIO descriptors") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202604041426.i2C1xqHk-lkp@intel.com/ Signed-off-by: Peng Fan Link: https://patch.msgid.link/20260413-ac97-v1-1-b44b9e084307@nxp.com Signed-off-by: Mark Brown --- sound/arm/pxa2xx-ac97-lib.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 1e114dbcf93c94..79eb557d4942f1 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -331,12 +331,15 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) if (dev->dev.of_node) { /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ rst_gpio = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH); - ret = PTR_ERR(rst_gpio); - if (ret == -ENOENT) - reset_gpio = -1; - else if (ret) - return ret; - reset_gpio = desc_to_gpio(rst_gpio); + if (IS_ERR(rst_gpio)) { + ret = PTR_ERR(rst_gpio); + if (ret == -ENOENT) + reset_gpio = -1; + else if (ret) + return ret; + } else { + reset_gpio = desc_to_gpio(rst_gpio); + } } else { if (cpu_is_pxa27x()) reset_gpio = 113; From bad4bd28abf4d7cb2adcb39cc0de789729d2cd69 Mon Sep 17 00:00:00 2001 From: Nishanth Sampath Kumar Date: Tue, 7 Apr 2026 16:39:27 -0700 Subject: [PATCH 692/700] regmap-i2c: add SMBus byte/word reg16 bus for adapters lacking I2C_FUNC_I2C AMD PIIX4 SMBus adapters, present on AMD SP5/EPYC-based platforms (including Cisco 8000 series routers), support SMBUS_BYTE_DATA and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK. When at24 (or any driver) requests a regmap with reg_bits=16 and val_bits=8 on such an adapter, regmap_get_i2c_bus() finds no matching bus and returns -ENOTSUPP. The existing regmap_i2c_smbus_i2c_block_reg16 bus type already implements 16-bit addressed reads using only write_byte_data() + read_byte() primitives, but its selection is gated on I2C_FUNC_SMBUS_I2C_BLOCK which these adapters lack. Add a new regmap_smbus_byte_word_reg16 bus that: READ: reuses regmap_i2c_smbus_i2c_read_reg16() -- sets the 16-bit address via write_byte_data(addr_lo, addr_hi), then reads bytes sequentially via read_byte() (EEPROM auto-increments). Requires only SMBUS_BYTE_DATA. WRITE: uses write_word_data(addr_hi, (data << 8) | addr_lo) to encode one data byte per SMBus WORD transaction. Requires only SMBUS_WORD_DATA. Single-byte writes only. The new bus is selected in regmap_get_i2c_bus() when reg_bits=16, val_bits=8, and the adapter has SMBUS_BYTE_DATA | SMBUS_WORD_DATA but not I2C_FUNC_I2C or SMBUS_I2C_BLOCK. The branch is placed after the existing I2C_BLOCK_reg16 check so adapters with full block support continue to use the faster path. This fixes at24 EEPROM probe failures on PIIX4: at24 3-0055: probe with driver at24 failed with error -524 No driver changes are required -- at24 already passes reg_bits=16 to devm_regmap_init_i2c(), which now succeeds. Signed-off-by: Nishanth Sampath Kumar Link: https://patch.msgid.link/20260407233927.498932-1-nissampa@cisco.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i2c.c | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index c9b39a02278e06..31e30dfced192f 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -303,6 +303,50 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = { .max_raw_write = I2C_SMBUS_BLOCK_MAX - 2, }; +/* + * SMBus byte/word reg16 support for adapters that have SMBUS_BYTE_DATA + * and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK, + * such as the AMD PIIX4. + * + * READ: set 16-bit EEPROM address via write_byte_data(addr_lo, addr_hi), + * then sequentially read bytes via read_byte() (EEPROM auto- + * increments the address pointer). Same as the I2C-block reg16 + * read path above. + * + * WRITE: encode the low address byte and data into a word transaction: + * write_word_data(addr_hi, (data_byte << 8) | addr_lo). + * Only single-byte writes are supported (one value per transaction). + */ +static int regmap_smbus_word_write_reg16(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + u8 addr_hi, addr_lo, val; + + /* + * data layout: [addr_hi, addr_lo, val0, val1, ...]. + * Only single-byte value writes are supported; multi-byte would + * require raw I2C (or repeated word writes with incrementing address). + */ + if (count != 3) + return -EINVAL; + + addr_hi = ((u8 *)data)[0]; + addr_lo = ((u8 *)data)[1]; + val = ((u8 *)data)[2]; + + return i2c_smbus_write_word_data(i2c, addr_hi, + cpu_to_le16(((u16)val << 8) | addr_lo)); +} + +static const struct regmap_bus regmap_smbus_byte_word_reg16 = { + .write = regmap_smbus_word_write_reg16, + .read = regmap_i2c_smbus_i2c_read_reg16, + .max_raw_read = I2C_SMBUS_BLOCK_MAX - 2, + .max_raw_write = 1, +}; + static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, const struct regmap_config *config) { @@ -321,6 +365,11 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) bus = ®map_i2c_smbus_i2c_block_reg16; + else if (config->val_bits == 8 && config->reg_bits == 16 && + i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + bus = ®map_smbus_byte_word_reg16; else if (config->val_bits == 16 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) From a47306a74c31557b1e5cab54642950bbb20294cb Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Tue, 14 Apr 2026 03:03:00 +0930 Subject: [PATCH 693/700] ALSA: usb-audio: Exclude Scarlett 18i20 1st Gen from SKIP_IFACE_SETUP Same issue as the other 1st Gen Scarletts: QUIRK_FLAG_SKIP_IFACE_SETUP causes distorted audio on the Scarlett 18i20 1st Gen (1235:800c). Fixes: 38c322068a26 ("ALSA: usb-audio: Add QUIRK_FLAG_SKIP_IFACE_SETUP") Reported-by: tucktuckg00se [https://github.com/geoffreybennett/linux-fcp/issues/54] Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/ad0ozNnkcFrcjVQz@m.b4.vu Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 519d9d1a2a4165..8fc36d1cfb9d2e 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2501,6 +2501,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_VALIDATE_RATES), DEVICE_FLG(0x1235, 0x8006, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x800a, 0), /* Focusrite Scarlett 2i4 1st Gen */ + DEVICE_FLG(0x1235, 0x800c, 0), /* Focusrite Scarlett 18i20 1st Gen */ DEVICE_FLG(0x1235, 0x8016, 0), /* Focusrite Scarlett 2i2 1st Gen */ DEVICE_FLG(0x1235, 0x801c, 0), /* Focusrite Scarlett Solo 1st Gen */ VENDOR_FLG(0x1235, /* Focusrite Novation */ From a7b56be59b47f4195ddc79ecab238c4401a60bbb Mon Sep 17 00:00:00 2001 From: Faye Nichols Date: Mon, 13 Apr 2026 15:20:25 -0600 Subject: [PATCH 694/700] ALSA: hda/realtek: Add quirk for Acer PT316-51S headset mic The Acer PT316-51S (PCI SSID 1025:160e) with ALC287 codec does not detect the headset microphone due to missing BIOS pin configuration for pin 0x19. Apply ALC2XX_FIXUP_HEADSET_MIC to enable it. Signed-off-by: Faye Nichols Link: https://patch.msgid.link/20260413212645.117119-1-faye.opensource@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 45bbd63370d6e9..0579762a191ad2 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6711,6 +6711,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), From 46c862f5419e0a86b60b9f9558d247f6084c99f9 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 14 Apr 2026 15:44:04 +0800 Subject: [PATCH 695/700] ALSA: hda/realtek - fixed speaker no sound update Fixed speaker has pop noise on Lenovo Thinkpad X11 Carbon Gen 12. Fixes: 630fbc6e870e ("ALSA: hda/realtek - fixed speaker no sound") Reported-and-tested-by: Jeremy Bethmont Closes: https://lore.kernel.org/CAC88DfsHrhyhy0Pn1O-z9egBvMYu=6NYgcvcC6KCgwh_-Ldkxg@mail.gmail.com Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 0579762a191ad2..b33f425763f92a 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3654,22 +3654,11 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action) { - static const struct coef_fw dis_coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), - WRITE_COEF(0x28, 0x0004), WRITE_COEF(0x29, 0xb023), - }; /* Disable AMP silence detection */ - static const struct coef_fw en_coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), - WRITE_COEF(0x28, 0x0084), WRITE_COEF(0x29, 0xb023), - }; /* Enable AMP silence detection */ - switch (action) { case HDA_GEN_PCM_ACT_OPEN: - alc_process_coef_fw(codec, dis_coefs); alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ break; case HDA_GEN_PCM_ACT_CLOSE: - alc_process_coef_fw(codec, en_coefs); alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ break; } @@ -3692,10 +3681,15 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), }; + static const struct coef_fw dis_coefs[] = { + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), + WRITE_COEF(0x28, 0x0004), WRITE_COEF(0x29, 0xb023), + }; /* Disable AMP silence detection */ if (action != HDA_FIXUP_ACT_PRE_PROBE) return; alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); + alc_process_coef_fw(codec, dis_coefs); alc_process_coef_fw(codec, coefs); spec->power_hook = alc287_s4_power_gpio3_default; spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; From a1ed2ec1c5458b4a99765439cb595dd0e026a352 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Apr 2026 11:33:34 +0200 Subject: [PATCH 696/700] ALSA: usb-audio: Fix missing error handling for get_min_max*() The recent fix to add the error return value check from get_min_max*() missed one case in build_audio_procunit() where no error value is set. This may lead to an uninitialized variable and confuse the caller (although this wouldn't happen practically because err is set for the loop of num_ins at the beginning of the funciton). Fix it by setting "err = 0" properly at the missing case, too. Fixes: 4f55a85cd4fc ("ALSA: usb-audio: Add error checks against get_min_max*()") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/ad36dGpCBTGsyFr_@stanley.mountain Link: https://patch.msgid.link/20260414093336.305464-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index d4ef45bf53d77c..aa6ea3be100a6b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2687,6 +2687,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval->max = control_spec[0]; cval->res = 1; cval->initialized = 1; + err = 0; break; } From cf162476f7e082662691e75f96bfc99c6a64810d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 14 Apr 2026 15:14:41 +0800 Subject: [PATCH 697/700] ASoC: rt1320: fix the warning 'rae_fw' from request_firmware() not released New smatch warnings: sound/soc/codecs/rt1320-sdw.c:1575 rt1320_rae_load() warn: 'rae_fw' from request_firmware() not released on lines: 1575. Fixes: 22937af75abb ("ASoC: rt1320: support RAE parameters loading") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202604111548.EL450PMb-lkp@intel.com/ Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260414071441.1524039-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index b0aeeab26bd99b..192faa431b5e9c 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1498,6 +1498,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } if (!retry && !(value & 0x40)) { dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); return -ETIMEDOUT; } From ab463b4655857b1865c611ff5fbcb752cd804b0b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 13 Apr 2026 14:07:59 +0800 Subject: [PATCH 698/700] ASoC: SOF: Intel: NVL: add platform name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform name will be used in the topology name. Fixes: 1800bcdc68ead ("ASoC: SOF: Intel: add support for Nova Lake NVL") Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260413060800.3156425-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/nvl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c index 0d763998558f0c..0ee3507824e51a 100644 --- a/sound/soc/sof/intel/nvl.c +++ b/sound/soc/sof/intel/nvl.c @@ -48,6 +48,7 @@ const struct sof_intel_dsp_desc nvl_chip_info = { .power_down_dsp = mtl_power_down_dsp, .disable_interrupts = lnl_dsp_disable_interrupts, .hw_ip_version = SOF_INTEL_ACE_4_0, + .platform = "nvl", }; const struct sof_intel_dsp_desc nvl_s_chip_info = { From a158fe7b0c817eebcf2871fe1306347a376030e8 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 13 Apr 2026 14:08:00 +0800 Subject: [PATCH 699/700] ASoC: SOF: Intel: NVL-S: add platform name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform name will be used in the topology name. Fixes: d3df422f66e8a ("ASoC: SOF: Intel: add initial support for NVL-S") Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260413060800.3156425-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/nvl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c index 0ee3507824e51a..86d2e1aa0eece8 100644 --- a/sound/soc/sof/intel/nvl.c +++ b/sound/soc/sof/intel/nvl.c @@ -73,6 +73,7 @@ const struct sof_intel_dsp_desc nvl_s_chip_info = { .power_down_dsp = mtl_power_down_dsp, .disable_interrupts = lnl_dsp_disable_interrupts, .hw_ip_version = SOF_INTEL_ACE_4_0, + .platform = "nvl", }; MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); From e0da8a8cac74f4b9f577979d131f0d2b88a84487 Mon Sep 17 00:00:00 2001 From: Ziqing Chen Date: Tue, 14 Apr 2026 21:24:37 +0800 Subject: [PATCH 700/700] ALSA: control: Validate buf_len before strnlen() in snd_ctl_elem_init_enum_names() snd_ctl_elem_init_enum_names() advances pointer p through the names buffer while decrementing buf_len. If buf_len reaches zero but items remain, the next iteration calls strnlen(p, 0). While strnlen(p, 0) returns 0 and would hit the existing name_len == 0 error path, CONFIG_FORTIFY_SOURCE's fortified strnlen() first checks maxlen against __builtin_dynamic_object_size(). When Clang loses track of p's object size inside the loop, this triggers a BRK exception panic before the return value is examined. Add a buf_len == 0 guard at the loop entry to prevent calling fortified strnlen() on an exhausted buffer. Found by kernel fuzz testing through Xiaomi Smartphone. Fixes: 8d448162bda5 ("ALSA: control: add support for ENUMERATED user space controls") Cc: stable@vger.kernel.org Signed-off-by: Ziqing Chen Link: https://patch.msgid.link/20260414132437.261304-1-chenziqing@xiaomi.com Signed-off-by: Takashi Iwai --- sound/core/control.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/control.c b/sound/core/control.c index 374e703d15a91c..5e51857635e62a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1648,6 +1648,10 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue) /* check that there are enough valid names */ p = names; for (i = 0; i < ue->info.value.enumerated.items; ++i) { + if (buf_len == 0) { + kvfree(names); + return -EINVAL; + } name_len = strnlen(p, buf_len); if (name_len == 0 || name_len >= 64 || name_len == buf_len) { kvfree(names);